text: /*
   * FreeRTOS+TCP V2.4.0
   * Copyright (C) 2021 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
   */
  
  /* Standard includes. */
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <stdarg.h>
  
  /* FreeRTOS includes. */
  #include "FreeRTOS.h"
  #include "task.h"
  #include "queue.h"
  #include "semphr.h"
  
  /* FreeRTOS+TCP includes. */
  #include "FreeRTOS_IP.h"
  #include "FreeRTOS_Sockets.h"
  #include "FreeRTOS_IP_Private.h"
  #include "NetworkBufferManagement.h"
  #include "NetworkInterface.h"
  
  #include "sam4e_xplained_pro.h"
  #include "hr_gettime.h"
  #include "conf_eth.h"
  #include "ksz8851snl.h"
  #include "ksz8851snl_reg.h"
  
  /* Some files from the Atmel Software Framework */
  #include <sysclk.h>
  #include <pdc/pdc.h>
  #include <spi/spi.h>
  
  /*
   *  Sending a packet:
   *
   *      1) Called by UP-task, add buffer to the TX-list:
   *          xNetworkInterfaceOutput()
   *              tx_buffers[ us_tx_head ] = pxNetworkBuffer;
   *              tx_busy[ us_tx_head ] = pdTRUE;
   *              us_tx_head++;
   *
   *      2) Called by EMAC-Task: start SPI transfer
   *          ksz8851snl_update()
   *          if( ul_spi_pdc_status == SPI_PDC_IDLE )
   *          {
   *              if( ( tx_busy[ us_tx_tail ] != pdFALSE ) &&
   *                  ( us_pending_frame == 0 ) &&
   *                  ( ul_had_intn_interrupt == 0 ) )
   *              {
   *                  // disable all interrupts.
   *                  ksz8851_reg_write( REG_INT_MASK, 0 );
   *                  Bring KSZ8851SNL_CSN_GPIO low
   *                  ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength );
   *                  ul_spi_pdc_status = SPI_PDC_TX_START;
   *                  tx_cur_buffer = pxNetworkBuffer;
   *              }
   *          }
   *      3) Wait for SPI RXBUFF interrupt
   *          SPI_Handler()
   *              if( ul_spi_pdc_status == SPI_PDC_TX_START )
   *              {
   *                  if( SPI_Status & SPI_SR_RXBUFF )
   *                  {
   *                      ul_spi_pdc_status = SPI_PDC_TX_COMPLETE;
   *                  }
   *              }
   *
   *      4) Called by EMAC-Task: finish SPI transfer
   *          ksz8851snl_update()
   *              if( ul_spi_pdc_status == SPI_PDC_TX_COMPLETE )
   *              {
   *                  ul_spi_pdc_status = SPI_PDC_IDLE;
   *                  Bring KSZ8851SNL_CSN_GPIO high
   *                  // TX step12: disable TXQ write access.
   *                  ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );
   *                  // TX step12.1: enqueue frame in TXQ.
   *                  ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE );
   *
   *                  // RX step13: enable INT_RX flag.
   *                  ksz8851_reg_write( REG_INT_MASK, INT_RX );
   *
   *                  // Buffer sent, free the corresponding buffer and mark descriptor as owned by software.
   *                  vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
   *
   *                  tx_buffers[ us_tx_tail ] = NULL;
   *                  tx_busy[ us_tx_tail ] = pdFALSE;
   *                  us_tx_tail++
   *              }
   *
   *  Receiving a packet:
   *
   *      1) Wait for a INTN interrupt
   *          INTN_Handler()
   *              ul_had_intn_interrupt = 1
   *              vTaskNotifyGiveFromISR();	// Wake up the EMAC task
   *
   *      2) Called by EMAC-Task: check for new fragments and start SPI transfer
   *          ksz8851snl_update()
   *              if( ul_spi_pdc_status == SPI_PDC_IDLE )
   *              {
   *                  if( ( ul_had_intn_interrupt != 0 ) || ( us_pending_frame > 0 ) )
   *                  {
   *                      if( us_pending_frame == 0 )
   *                      {
   *                          us_pending_frame = ksz8851_reg_read(REG_RX_FRAME_CNT_THRES) >> 8;
   *                          if( us_pending_frame == 0 )
   *                          {
   *                              break;
   *                          }
   *                      }
   *                      // RX step2: disable all interrupts.
   *                      ksz8851_reg_write( REG_INT_MASK, 0 );
   *                      Check if there is a valid packet: REG_RX_FHR_STATUS
   *                      Read the length of the next fragment: REG_RX_FHR_BYTE_CNT
   *                      ul_spi_pdc_status = SPI_PDC_RX_START;
   *                      gpio_set_pin_low(KSZ8851SNL_CSN_GPIO);
   *                      // Start SPI data transfer
   *                      ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer, xReadLength );
   *                  }
   *              }
   *
   *      3) Wait for SPI RXBUFF interrupt
   *          SPI_Handler()
   *          if( ul_spi_pdc_status == SPI_PDC_RX_START:
   *          {
   *              if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 )
   *              {
   *                  // Transfer complete, disable SPI RXBUFF interrupt.
   *                  spi_disable_interrupt( KSZ8851SNL_SPI, SPI_IDR_RXBUFF );
   *
   *                  ul_spi_pdc_status = SPI_PDC_RX_COMPLETE;
   *              }
   *          }
   *      }
   *
   *      4) Finish SPI transfer
   *          ksz8851snl_update()
   *              if( ul_spi_pdc_status == SPI_PDC_RX_COMPLETE )
   *              {
   *                  ul_spi_pdc_status = SPI_PDC_IDLE;
   *                  Bring KSZ8851SNL_CSN_GPIO high
   *                  // RX step21: end RXQ read access.
   *                  ksz8851_reg_clrbits(REG_RXQ_CMD, RXQ_START);
   *                  // RX step22-23: update frame count to be read.
   *                  us_pending_frame--
   *                  // RX step24: enable INT_RX flag if transfer complete.
   *                  if( us_pending_frame == 0 )
   *                  {
   *                      // Allow more RX interrupts.
   *                      ksz8851_reg_write( REG_INT_MASK, INT_RX );
   *                  }
   *
   *                  // Mark descriptor ready to be read.
   *                  rx_ready[ rxHead ] = pdTRUE;
   *                  rxHead++
   *              }
   */
  
  #define PHY_REG_00_BMCR         0x00    /* Basic mode control register */
  #define PHY_REG_01_BMSR         0x01    /* Basic mode status register */
  #define PHY_REG_02_PHYSID1      0x02    /* PHYS ID 1 */
  #define PHY_REG_03_PHYSID2      0x03    /* PHYS ID 2 */
  #define PHY_REG_04_ADVERTISE    0x04    /* Advertisement control reg */
  #define PHY_REG_05_LPA          0x05    /* Link partner ability reg */
  #define PHY_REG_06_ANER         0x06    /*	6	RW		Auto-Negotiation Expansion Register */
  #define PHY_REG_07_ANNPTR       0x07    /*	7	RW		Auto-Negotiation Next Page TX */
  #define PHY_REG_08_RESERVED0    0x08    /* 0x08..0x0Fh	8-15	RW		RESERVED */
  
  #define BMSR_LINK_STATUS        0x0004  /*!< Link status */
  
  #ifndef PHY_LS_HIGH_CHECK_TIME_MS
  
  /* Check if the LinkSStatus in the PHY is still high after 15 seconds of not
   * receiving packets. */
      #define PHY_LS_HIGH_CHECK_TIME_MS    15000
  #endif
  
  #ifndef PHY_LS_LOW_CHECK_TIME_MS
      /* Check if the LinkSStatus in the PHY is still low every second. */
      #define PHY_LS_LOW_CHECK_TIME_MS    1000
  #endif
  
  /* Interrupt events to process.  Currently only the Rx event is processed
   * although code for other events is included to allow for possible future
   * expansion. */
  #define EMAC_IF_RX_EVENT          1UL
  #define EMAC_IF_TX_EVENT          2UL
  #define EMAC_IF_ERR_EVENT         4UL
  #define EMAC_IF_ALL_EVENT         ( EMAC_IF_RX_EVENT | EMAC_IF_TX_EVENT | EMAC_IF_ERR_EVENT )
  
  #define ETHERNET_CONF_PHY_ADDR    BOARD_GMAC_PHY_ADDR
  
  #ifdef ipconfigHAS_TX_CRC_OFFLOADING
      #undef ipconfigHAS_TX_CRC_OFFLOADING
  #endif
  /* Override this define because the KSZ8851 is programmed to set all outgoing CRC's */
  #define ipconfigHAS_TX_CRC_OFFLOADING    1
  
  #ifndef EMAC_MAX_BLOCK_TIME_MS
      #define EMAC_MAX_BLOCK_TIME_MS       100ul
  #endif
  
  /* Default the size of the stack used by the EMAC deferred handler task to 4x
   *  the size of the stack used by the idle task - but allow this to be overridden in
   *  FreeRTOSConfig.h as configMINIMAL_STACK_SIZE is a user definable constant. */
  #ifndef configEMAC_TASK_STACK_SIZE
      #define configEMAC_TASK_STACK_SIZE    ( 6 * configMINIMAL_STACK_SIZE )
  #endif
  
  #define SPI_PDC_IDLE                      0
  #define SPI_PDC_RX_START                  1
  #define SPI_PDC_TX_ERROR                  2
  #define SPI_PDC_RX_COMPLETE               3
  #define SPI_PDC_TX_START                  4
  #define SPI_PDC_RX_ERROR                  5
  #define SPI_PDC_TX_COMPLETE               6
  
  /**
   * ksz8851snl driver structure.
   */
  typedef struct
  {
      /** Set to 1 when owner is software (ready to read), 0 for Micrel. */
      uint32_t rx_ready[ MICREL_RX_BUFFERS ];
      /** Set to 1 when owner is Micrel, 0 for software. */
      uint32_t tx_busy[ MICREL_TX_BUFFERS ];
      /** RX NetworkBufferDescriptor_t pointer list */
      NetworkBufferDescriptor_t * rx_buffers[ MICREL_RX_BUFFERS ];
      /** TX NetworkBufferDescriptor_t pointer list */
      NetworkBufferDescriptor_t * tx_buffers[ MICREL_TX_BUFFERS ];
      NetworkBufferDescriptor_t * tx_cur_buffer;
  
      /** Circular buffer head pointer for packet received. */
      uint32_t us_rx_head;
      /** Circular buffer tail pointer for packet to be read. */
      uint32_t us_rx_tail;
      /** Circular buffer head pointer by upper layer (buffer to be sent). */
      uint32_t us_tx_head;
      /** Circular buffer tail pointer incremented by handlers (buffer sent). */
      uint32_t us_tx_tail;
  
      uint32_t ul_total_tx;
      uint32_t ul_total_rx;
      uint32_t tx_space;
  
      /** Still experimental: hash table to allow certain multicast addresses. */
      uint16_t pusHashTable[ 4 ];
  
      /* ul_spi_pdc_status has "SPI_PDC_xxx" values. */
      volatile uint32_t ul_spi_pdc_status;
  
      /* ul_had_intn_interrupt becomes true within the INTN interrupt. */
      volatile uint32_t ul_had_intn_interrupt;
  
      uint16_t us_pending_frame;
  } xKSZ8851_Device_t;
  
  /* SPI PDC register base.
   * Declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */
  extern Pdc * g_p_spi_pdc;
  
  /* Temporary buffer for PDC reception.
   * declared in ASF\sam\components\ksz8851snl\ksz8851snl.c */
  extern uint8_t tmpbuf[ 1536 ];
  
  COMPILER_ALIGNED( 8 )
  static xKSZ8851_Device_t xMicrelDevice;
  
  static TaskHandle_t xTransmitHandle;
  
  /*-----------------------------------------------------------*/
  
  /*
   * Wait a fixed time for the link status to indicate the network is up.
   */
  static BaseType_t xGMACWaitLS( TickType_t xMaxTime );
  
  /*
   * A deferred interrupt handler task that processes GMAC interrupts.
   */
  static void prvEMACHandlerTask( void * pvParameters );
  
  /*
   * Try to obtain an Rx packet from the hardware.
   */
  static uint32_t prvEMACRxPoll( void );
  
  static inline unsigned long ulReadMDIO( unsigned uAddress );
  
  static void ksz8851snl_low_level_init( void );
  
  static NetworkBufferDescriptor_t * ksz8851snl_low_level_input( void );
  
  /*-----------------------------------------------------------*/
  
  /* Bit map of outstanding ETH interrupt events for processing.  Currently only
   * the Rx interrupt is handled, although code is included for other events to
   * enable future expansion. */
  static volatile uint32_t ulISREvents;
  
  /* A copy of PHY register 1: 'PHY_REG_01_BMSR' */
  static uint32_t ulPHYLinkStatus = 0;
  static volatile BaseType_t xGMACSwitchRequired;
  
  static void ksz8851snl_update( void );
  
  static void ksz8851snl_rx_init( void );
  
  static void ksz8851snl_tx_init( void );
  
  /* Holds the handle of the task used as a deferred interrupt processor.  The
   * handle is used so direct notifications can be sent to the task for all EMAC/DMA
   * related interrupts. */
  TaskHandle_t xEMACTaskHandle = NULL;
  
  
  /*-----------------------------------------------------------*/
  
  BaseType_t xNetworkInterfaceInitialise( void )
  {
      const TickType_t x5_Seconds = 5000UL;
  
      if( xEMACTaskHandle == NULL )
      {
          ksz8851snl_low_level_init();
  
          /* Wait at most 5 seconds for a Link Status in the PHY. */
          xGMACWaitLS( pdMS_TO_TICKS( x5_Seconds ) );
  
          /* The handler task is created at the highest possible priority to
           * ensure the interrupt handler can return directly to it. */
          xTaskCreate( prvEMACHandlerTask, "KSZ8851", configEMAC_TASK_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, &xEMACTaskHandle );
          configASSERT( xEMACTaskHandle != NULL );
      }
  
      /* When returning non-zero, the stack will become active and
       * start DHCP (in configured) */
      ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR );
  
      return ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0;
  }
  /*-----------------------------------------------------------*/
  
  BaseType_t xGetPhyLinkStatus( void )
  {
      BaseType_t xResult;
  
      /* This function returns true if the Link Status in the PHY is high. */
      if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
      {
          xResult = pdTRUE;
      }
      else
      {
          xResult = pdFALSE;
      }
  
      return xResult;
  }
  /*-----------------------------------------------------------*/
  
  BaseType_t xNetworkInterfaceOutput( NetworkBufferDescriptor_t * const pxNetworkBuffer,
                                      BaseType_t bReleaseAfterSend )
  {
      BaseType_t xResult = pdFALSE;
      int txHead = xMicrelDevice.us_tx_head;
  
      /* Make sure the next descriptor is free. */
      if( xMicrelDevice.tx_busy[ txHead ] != pdFALSE )
      {
          /* All TX buffers busy. */
      }
      else if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) == 0 )
      {
          /* Output: LS low. */
      }
      else
      {
          /* Pass the packet. */
          xMicrelDevice.tx_buffers[ txHead ] = pxNetworkBuffer;
          /* The descriptor is now owned by Micrel. */
          xMicrelDevice.tx_busy[ txHead ] = pdTRUE;
  
          /* Move the head pointer. */
          if( ++txHead == MICREL_TX_BUFFERS )
          {
              txHead = 0;
          }
  
          xMicrelDevice.us_tx_head = txHead;
  
          if( xEMACTaskHandle != NULL )
          {
              xTaskNotifyGive( xEMACTaskHandle );
          }
  
          #if ( ipconfigZERO_COPY_TX_DRIVER != 1 )
          #warning Please ipconfigZERO_COPY_TX_DRIVER as 1
          #endif
          configASSERT( bReleaseAfterSend != pdFALSE );
          xResult = pdTRUE;
      }
  
      if( ( xResult == pdFALSE ) && ( bReleaseAfterSend != pdFALSE ) )
      {
          vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
      }
  
      return xResult;
  }
  /*-----------------------------------------------------------*/
  
  /* This Micrel has numbered it's PHY registers in a different way.
   * Translate the register index. */
  static int ks8851_phy_reg( int reg )
  {
      switch( reg )
      {
          case PHY_REG_00_BMCR:
              return REG_PHY_CNTL; /* P1MBCR; */
  
          case PHY_REG_01_BMSR:
              return REG_PHY_STATUS;
  
          case PHY_REG_02_PHYSID1:
              return REG_PHY_ID_LOW;
  
          case PHY_REG_03_PHYSID2:
              return REG_PHY_ID_HIGH;
  
          case PHY_REG_04_ADVERTISE:
              return REG_PHY_AUTO_NEGOTIATION;
  
          case PHY_REG_05_LPA:
              return REG_PHY_REMOTE_CAPABILITY;
      }
  
      return 0x0;
  }
  /*-----------------------------------------------------------*/
  
  static inline unsigned long ulReadMDIO( unsigned uAddress )
  {
      uint16_t usPHYStatus;
      int ks8851_reg = ks8851_phy_reg( uAddress );
  
      if( ks8851_reg != 0 )
      {
          usPHYStatus = ksz8851_reg_read( ks8851_reg );
      }
      else
      {
          /* Other addresses not yet implemented. */
          usPHYStatus = 0;
      }
  
      return usPHYStatus;
  }
  /*-----------------------------------------------------------*/
  
  static BaseType_t xGMACWaitLS( TickType_t xMaxTime )
  {
      TickType_t xStartTime = xTaskGetTickCount();
      TickType_t xEndTime;
      BaseType_t xReturn;
      const TickType_t xShortTime = pdMS_TO_TICKS( 100UL );
      const uint32_t ulHz_Per_MHz = 1000000UL;
  
      for( ; ; )
      {
          xEndTime = xTaskGetTickCount();
  
          if( ( xEndTime - xStartTime ) > xMaxTime )
          {
              /* Waited more than xMaxTime, return. */
              xReturn = pdFALSE;
              break;
          }
  
          /* Check the link status again. */
          ulPHYLinkStatus = ulReadMDIO( PHY_REG_01_BMSR );
  
          if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
          {
              /* Link is up - return. */
              xReturn = pdTRUE;
              break;
          }
  
          /* Link is down - wait in the Blocked state for a short while (to allow
           * other tasks to execute) before checking again. */
          vTaskDelay( xShortTime );
      }
  
      FreeRTOS_printf( ( "xGMACWaitLS: %ld freq %lu Mz\n",
                         xReturn,
                         sysclk_get_cpu_hz() / ulHz_Per_MHz ) );
  
      return xReturn;
  }
  /*-----------------------------------------------------------*/
  
  static void vPioSetPinHigh( uint32_t ul_pin )
  {
      Pio * p_pio = ( Pio * ) ( ( uint32_t ) PIOA + ( PIO_DELTA * ( ul_pin >> 5 ) ) );
  
      /* Value to be driven on the I/O line: 1. */
      p_pio->PIO_SODR = 1 << ( ul_pin & 0x1F );
  }
  
  /**
   * \brief Handler for SPI interrupt.
   */
  void SPI_Handler( void )
  {
      BaseType_t xDoWakeup = pdFALSE;
      BaseType_t xKSZTaskWoken = pdFALSE;
      uint32_t ulCurrentSPIStatus;
      uint32_t ulEnabledSPIStatus;
  
      ulCurrentSPIStatus = spi_read_status( KSZ8851SNL_SPI );
      ulEnabledSPIStatus = spi_read_interrupt_mask( KSZ8851SNL_SPI );
      ulCurrentSPIStatus &= ulEnabledSPIStatus;
      spi_disable_interrupt( KSZ8851SNL_SPI, ulCurrentSPIStatus );
  
      switch( xMicrelDevice.ul_spi_pdc_status )
      {
          case SPI_PDC_RX_START:
  
              if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 )
              {
                  pdc_disable_transfer( g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS );
                  xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_ERROR;
                  xDoWakeup = pdTRUE;
              }
              else
              {
                  if( ( ulCurrentSPIStatus & SPI_SR_RXBUFF ) != 0 )
                  {
                      xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_COMPLETE;
                      xDoWakeup = pdTRUE;
                  }
              }
  
              break;
  
          case SPI_PDC_TX_START:
  
              /* Middle of TX. */
              if( ( ulCurrentSPIStatus & SPI_SR_OVRES ) != 0 )
              {
                  pdc_disable_transfer( g_p_spi_pdc, PERIPH_PTCR_RXTDIS | PERIPH_PTCR_TXTDIS );
                  xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_ERROR;
                  xDoWakeup = pdTRUE;
              }
              else
              {
                  if( ( ulCurrentSPIStatus & SPI_SR_ENDRX ) != 0 )
                  {
                      /* Enable RX complete interrupt. */
                      spi_enable_interrupt( KSZ8851SNL_SPI, SPI_IER_RXBUFF );
                  }
  
                  /* End of TX. */
                  if( ( ulCurrentSPIStatus & SPI_END_OF_TX ) != 0 )
                  {
                      xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_COMPLETE;
                      xDoWakeup = pdTRUE;
                  }
              }
  
              break;
      } /* switch( xMicrelDevice.ul_spi_pdc_status ) */
  
      if( xDoWakeup != pdFALSE )
      {
          if( xEMACTaskHandle != NULL )
          {
              vTaskNotifyGiveFromISR( xEMACTaskHandle, ( BaseType_t * ) &xKSZTaskWoken );
          }
      }
      else
      {
      }
  
      portEND_SWITCHING_ISR( xKSZTaskWoken );
  }
  /*-----------------------------------------------------------*/
  
  static void INTN_Handler( uint32_t id,
                            uint32_t mask )
  {
      BaseType_t xKSZTaskWoken = pdFALSE;
  
      if( ( id == INTN_ID ) &&
          ( mask == INTN_PIN_MSK ) )
      {
          /* Clear the PIO interrupt flags. */
          pio_get_interrupt_status( INTN_PIO );
  
          /* Set the INTN flag. */
          xMicrelDevice.ul_had_intn_interrupt++;
  
          if( xEMACTaskHandle != NULL )
          {
              vTaskNotifyGiveFromISR( xEMACTaskHandle, &( xKSZTaskWoken ) );
          }
      }
  
      portEND_SWITCHING_ISR( xKSZTaskWoken );
  }
  /*-----------------------------------------------------------*/
  
  /**
   * \brief Populate the RX descriptor ring buffers with pbufs.
   *
   * \param p_ksz8851snl_dev Pointer to driver data structure.
   */
  static void ksz8851snl_rx_populate_queue( void )
  {
      uint32_t ul_index = 0;
      NetworkBufferDescriptor_t * pxNetworkBuffer;
  
      /* Set up the RX descriptors */
      for( ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++ )
      {
          if( xMicrelDevice.rx_buffers[ ul_index ] == NULL )
          {
              /* Allocate a new NetworkBufferDescriptor_t with the maximum size. */
              pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( ipconfigNETWORK_MTU + 36, 100 );
  
              if( pxNetworkBuffer == NULL )
              {
                  FreeRTOS_printf( ( "ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t allocation failure\n" ) );
                  configASSERT( 1 == 2 );
              }
  
              /* Make sure lwIP is well configured so one NetworkBufferDescriptor_t can contain the maximum packet size. */
              /*LWIP_ASSERT("ksz8851snl_rx_populate_queue: NetworkBufferDescriptor_t size too small!", pbuf_clen(pxNetworkBuffer) <= 1); */
  
              /* Save NetworkBufferDescriptor_t pointer to be sent to lwIP upper layer. */
              xMicrelDevice.rx_buffers[ ul_index ] = pxNetworkBuffer;
              /* Pass it to Micrel for reception. */
              xMicrelDevice.rx_ready[ ul_index ] = pdFALSE;
          }
      }
  }
  
  unsigned tx_space, wait_tx_space, tx_status, fhr_status;
  unsigned rx_debug = 0;
  
  /**
   * \brief Update Micrel state machine and perform required actions.
   *
   * \param netif the lwIP network interface structure for this ethernetif.
   */
  static void ksz8851snl_update()
  {
      uint16_t txmir = 0;
  
  /* Check for free PDC. */
      switch( xMicrelDevice.ul_spi_pdc_status )
      {
          case SPI_PDC_TX_ERROR:
             {
                 uint32_t ulValue;
                 /*		/ * TX step11: end TX transfer. * / */
                 gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
  
                 vTaskDelay( 2 );
                 gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );
                 vTaskDelay( 1 );
                 gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
                 vTaskDelay( 1 );
  
                 /* Disable asynchronous transfer mode. */
                 xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;
  
                 /* TX step12: disable TXQ write access. */
                 ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );
  
                 ulValue = ksz8851snl_reset_tx();
  
                 xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK;
  
                 FreeRTOS_printf( ( "SPI_PDC_TX_ERROR %02X\n", ulValue ) );
             }
             break;
  
          case SPI_PDC_RX_ERROR:
             {
                 uint32_t ulValue;
                 /* TX step11: end TX transfer. */
                 gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
  
                 vTaskDelay( 2 );
                 gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );
                 vTaskDelay( 1 );
                 gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
                 vTaskDelay( 1 );
  
                 /* Disable asynchronous transfer mode. */
                 xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;
  
                 /* TX step12: disable TXQ write access. */
                 ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );
  
                 /*ulValue = ksz8851snl_reset_rx(); */
                 ulValue = ksz8851snl_reinit();
  
                 xGMACWaitLS( pdMS_TO_TICKS( 5000UL ) );
  
                 FreeRTOS_printf( ( "SPI_PDC_RX_ERROR %02X\n", ulValue ) );
             }
             break;
      }
  
      switch( xMicrelDevice.ul_spi_pdc_status )
      {
          case SPI_PDC_IDLE:
             {
                 int txTail = xMicrelDevice.us_tx_tail;
  
                 /*
                  * ========================== Handle RX ==========================
                  */
                 if( ( xMicrelDevice.ul_had_intn_interrupt != 0 ) || ( xMicrelDevice.us_pending_frame > 0 ) )
                 {
                     int rxHead = xMicrelDevice.us_rx_head;
                     NetworkBufferDescriptor_t * pxNetworkBuffer;
                     #warning try
                     xMicrelDevice.ul_had_intn_interrupt = 0;
  
                     if( xMicrelDevice.us_pending_frame == 0 )
                     {
                         uint16_t int_status;
                         /* RX step1: read interrupt status for INT_RX flag. */
                         int_status = ksz8851_reg_read( REG_INT_STATUS );
  
  
                         /* RX step2: disable all interrupts. */
                         ksz8851_reg_write( REG_INT_MASK, 0 );
  
                         /* RX step3: clear INT_RX flag. */
                         ksz8851_reg_setbits( REG_INT_STATUS, INT_RX );
  
                         /* RX step4-5: check for received frames. */
                         xMicrelDevice.us_pending_frame = ksz8851_reg_read( REG_RX_FRAME_CNT_THRES ) >> 8;
  
                         if( xMicrelDevice.us_pending_frame == 0 )
                         {
                             /* RX step24: enable INT_RX flag. */
                             ksz8851_reg_write( REG_INT_MASK, INT_RX );
                             return;
                         }
                     }
  
                     #warning try
                     xMicrelDevice.ul_had_intn_interrupt = 0;
  
                     /* Now xMicrelDevice.us_pending_frame != 0 */
  
                     /* Don't break Micrel state machine, wait for a free descriptor first! */
                     if( xMicrelDevice.rx_ready[ rxHead ] != pdFALSE )
                     {
                         FreeRTOS_printf( ( "ksz8851snl_update: out of free descriptor! [tail=%u head=%u]\n",
                                            xMicrelDevice.us_rx_tail, rxHead ) );
                         return;
                     }
  
                     pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxHead ];
  
                     if( pxNetworkBuffer == NULL )
                     {
                         ksz8851snl_rx_populate_queue();
                         FreeRTOS_printf( ( "ksz8851snl_update: no buffer set [head=%u]\n", rxHead ) );
                         return;
                     }
  
                     /* RX step6: get RX packet status. */
                     fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS );
  
                     if( ( ( fhr_status & RX_VALID ) == 0 ) || ( ( fhr_status & RX_ERRORS ) != 0 ) )
                     {
                         ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_CMD_FREE_PACKET );
                         FreeRTOS_printf( ( "ksz8851snl_update: RX packet error!\n" ) );
  
                         /* RX step4-5: check for received frames. */
                         xMicrelDevice.us_pending_frame = ksz8851_reg_read( REG_RX_FRAME_CNT_THRES ) >> 8;
  
                         if( xMicrelDevice.us_pending_frame == 0 )
                         {
                             /* RX step24: enable INT_RX flag. */
                             ksz8851_reg_write( REG_INT_MASK, INT_RX );
                         }
  
                         ulISREvents |= EMAC_IF_ERR_EVENT;
                     }
                     else
                     {
                         size_t xLength;
                         /* RX step7: read frame length. */
                         xLength = ksz8851_reg_read( REG_RX_FHR_BYTE_CNT ) & RX_BYTE_CNT_MASK;
  
                         /* RX step8: Drop packet if len is invalid or no descriptor available. */
                         if( xLength == 0 )
                         {
                             ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_CMD_FREE_PACKET );
                             FreeRTOS_printf( ( "ksz8851snl_update: RX bad len!\n" ) );
                             ulISREvents |= EMAC_IF_ERR_EVENT;
                         }
                         else
                         {
                             size_t xReadLength = xLength;
  
                             xMicrelDevice.ul_total_rx++;
                             /* RX step9: reset RX frame pointer. */
                             ksz8851_reg_clrbits( REG_RX_ADDR_PTR, ADDR_PTR_MASK );
  
                             /* RX step10: start RXQ read access. */
                             ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_START );
                             /* RX step11-17: start asynchronous FIFO read operation. */
                             xMicrelDevice.ul_spi_pdc_status = SPI_PDC_RX_START;
                             gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );
  
                             if( ( xReadLength & ( sizeof( size_t ) - 1 ) ) != 0 )
                             {
                                 xReadLength = ( xReadLength | ( sizeof( size_t ) - 1 ) ) + 1;
                             }
  
                             /* Pass the buffer minus 2 bytes, see ksz8851snl.c: RXQ_TWOBYTE_OFFSET. */
                             ksz8851_fifo_read( pxNetworkBuffer->pucEthernetBuffer - 2, xReadLength );
                             /* Remove CRC and update buffer length. */
                             xLength -= 4;
                             pxNetworkBuffer->xDataLength = xLength;
                             /* Wait for SPI interrupt to set status 'SPI_PDC_RX_COMPLETE'. */
                         }
                     }
  
                     break;
                 } /* ul_had_intn_interrupt || us_pending_frame */
  
                 /*
                  * ========================== Handle TX ==========================
                  */
  
                 /* Fetch next packet to be sent. */
                 if( ( xMicrelDevice.tx_busy[ txTail ] != pdFALSE ) &&
                     ( xMicrelDevice.us_pending_frame == 0 ) &&
                     ( xMicrelDevice.ul_had_intn_interrupt == 0 ) )
                 {
                     NetworkBufferDescriptor_t * pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ];
                     size_t xLength = pxNetworkBuffer->xDataLength;
                     int iIndex = xLength;
  
                     xLength = 4 * ( ( xLength + 3 ) / 4 );
  
                     while( iIndex < ( int ) xLength )
                     {
                         pxNetworkBuffer->pucEthernetBuffer[ iIndex ] = '\0';
                         iIndex++;
                     }
  
                     pxNetworkBuffer->xDataLength = xLength;
  
                     /* TX step1: check if TXQ memory size is available for transmit. */
                     txmir = ksz8851_reg_read( REG_TX_MEM_INFO );
                     txmir = txmir & TX_MEM_AVAILABLE_MASK;
  
                     if( txmir < ( xLength + 8 ) )
                     {
                         if( wait_tx_space == pdFALSE )
                         {
                             tx_status = ksz8851_reg_read( REG_TX_STATUS );
                             fhr_status = ksz8851_reg_read( REG_RX_FHR_STATUS );
                             wait_tx_space = pdTRUE;
                         }
  
                         /*return; */
                         rx_debug = 1;
                         tx_space = txmir;
                     }
                     else
                     {
                         tx_space = txmir;
  
                         /* TX step2: disable all interrupts. */
                         ksz8851_reg_write( REG_INT_MASK, 0 );
  
                         xMicrelDevice.tx_space -= xLength;
  
                         /* TX step3: enable TXQ write access. */
                         ksz8851_reg_setbits( REG_RXQ_CMD, RXQ_START );
                         /* TX step4-8: perform FIFO write operation. */
                         xMicrelDevice.ul_spi_pdc_status = SPI_PDC_TX_START;
                         xMicrelDevice.tx_cur_buffer = pxNetworkBuffer;
                         /* Bring SPI SS low. */
                         gpio_set_pin_low( KSZ8851SNL_CSN_GPIO );
                         xMicrelDevice.ul_total_tx++;
  
                         ksz8851_fifo_write( pxNetworkBuffer->pucEthernetBuffer, xLength, xLength );
                     }
                 }
             }
             break; /* SPI_PDC_IDLE */
  
          case SPI_PDC_RX_COMPLETE:
             {
                 int rxHead = xMicrelDevice.us_rx_head;
                 /* RX step18-19: pad with dummy data to keep dword alignment. */
                 /* Packet lengths will be rounded up to a multiple of "sizeof size_t". */
  /*			xLength = xMicrelDevice.rx_buffers[ rxHead ]->xDataLength & 3; */
  /*			if( xLength != 0 ) */
  /*			{ */
  /*				ksz8851_fifo_dummy( 4 - xLength ); */
  /*			} */
  
                 /* RX step20: end RX transfer. */
                 gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
  
                 /* Disable asynchronous transfer mode. */
                 xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;
  
                 /* RX step21: end RXQ read access. */
                 ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );
  
                 /* RX step22-23: update frame count to be read. */
                 xMicrelDevice.us_pending_frame -= 1;
  
                 /* RX step24: enable INT_RX flag if transfer complete. */
                 if( xMicrelDevice.us_pending_frame == 0 )
                 {
                     ksz8851_reg_write( REG_INT_MASK, INT_RX );
                 }
  
                 /* Mark descriptor ready to be read. */
                 xMicrelDevice.rx_ready[ rxHead ] = pdTRUE;
  
                 if( ++rxHead == MICREL_RX_BUFFERS )
                 {
                     rxHead = 0;
                 }
  
                 xMicrelDevice.us_rx_head = rxHead;
  
                 if( rx_debug != 0 )
                 {
                     uint32_t txmir;
                     rx_debug = 0;
                     txmir = ksz8851_reg_read( REG_TX_MEM_INFO );
                     txmir = txmir & TX_MEM_AVAILABLE_MASK;
                 }
  
                 /* Tell prvEMACHandlerTask that RX packets are available. */
                 ulISREvents |= EMAC_IF_RX_EVENT;
             } /* case SPI_PDC_RX_COMPLETE */
             break;
  
          case SPI_PDC_TX_COMPLETE:
             {
                 int txTail = xMicrelDevice.us_tx_tail;
                 NetworkBufferDescriptor_t * pxNetworkBuffer = xMicrelDevice.tx_buffers[ txTail ];
  
                 size_t xLength;
                 /* TX step9-10: pad with dummy data to keep dword alignment. */
                 /* Not necessary: length is already a multiple of 4. */
                 xLength = pxNetworkBuffer->xDataLength & 3;
  
                 if( xLength != 0 )
                 {
  /*				ksz8851_fifo_dummy( 4 - xLength ); */
                 }
  
  /*			/ * TX step11: end TX transfer. * / */
                 gpio_set_pin_high( KSZ8851SNL_CSN_GPIO );
  
                 /* Disable asynchronous transfer mode. */
                 xMicrelDevice.ul_spi_pdc_status = SPI_PDC_IDLE;
  
                 /* TX step12: disable TXQ write access. */
                 ksz8851_reg_clrbits( REG_RXQ_CMD, RXQ_START );
  
                 xMicrelDevice.tx_space = ksz8851_reg_read( REG_TX_MEM_INFO ) & TX_MEM_AVAILABLE_MASK;
  
                 /* TX step12.1: enqueue frame in TXQ. */
                 ksz8851_reg_setbits( REG_TXQ_CMD, TXQ_ENQUEUE );
  
                 /* RX step13: enable INT_RX flag. */
  /*			ksz8851_reg_write( REG_INT_MASK, INT_RX ); */
                 /* Buffer sent, free the corresponding buffer and mark descriptor as owned by software. */
                 vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
  
                 xMicrelDevice.tx_buffers[ txTail ] = NULL;
                 xMicrelDevice.tx_busy[ txTail ] = pdFALSE;
  
                 if( ++txTail == MICREL_TX_BUFFERS )
                 {
                     txTail = 0;
                 }
  
                 xMicrelDevice.us_tx_tail = txTail;
  
                 /* Experiment. */
                 /*xMicrelDevice.ul_had_intn_interrupt = 1; */
                 if( xTransmitHandle != NULL )
                 {
                     xTaskNotifyGive( xTransmitHandle );
                 }
  
                 #warning moved downward
                 /* RX step13: enable INT_RX flag. */
                 ksz8851_reg_write( REG_INT_MASK, INT_RX );
                 /* Prevent the EMAC task from sleeping a single time. */
                 ulISREvents |= EMAC_IF_TX_EVENT;
             } /* case SPI_PDC_TX_COMPLETE */
             break;
      }        /* switch( xMicrelDevice.ul_spi_pdc_status ) */
  }
  
  /**
   * \brief Set up the RX descriptor ring buffers.
   *
   * This function sets up the descriptor list used for RX packets.
   *
   */
  static void ksz8851snl_rx_init()
  {
      uint32_t ul_index = 0;
  
      /* Init pointer index. */
      xMicrelDevice.us_rx_head = 0;
      xMicrelDevice.us_rx_tail = 0;
  
      /* Set up the RX descriptors. */
      for( ul_index = 0; ul_index < MICREL_RX_BUFFERS; ul_index++ )
      {
          xMicrelDevice.rx_buffers[ ul_index ] = NULL;
          xMicrelDevice.rx_ready[ ul_index ] = pdFALSE;
      }
  
      /* Build RX buffer and descriptors. */
      ksz8851snl_rx_populate_queue();
  }
  
  /**
   * \brief Set up the TX descriptor ring buffers.
   *
   * This function sets up the descriptor list used for TX packets.
   *
   */
  static void ksz8851snl_tx_init()
  {
      uint32_t ul_index = 0;
  
      /* Init TX index pointer. */
      xMicrelDevice.us_tx_head = 0;
      xMicrelDevice.us_tx_tail = 0;
  
      /* Set up the TX descriptors */
      for( ul_index = 0; ul_index < MICREL_TX_BUFFERS; ul_index++ )
      {
          xMicrelDevice.tx_busy[ ul_index ] = pdFALSE;
      }
  
      xMicrelDevice.tx_space = 6144;
  }
  
  /**
   * \brief Initialize ksz8851snl ethernet controller.
   *
   * \note Called from ethernetif_init().
   *
   * \param netif the lwIP network interface structure for this ethernetif.
   */
  static void ksz8851snl_low_level_init( void )
  {
      ksz8851snl_rx_init();
      ksz8851snl_tx_init();
  
      /* Enable NVIC interrupts. */
      NVIC_SetPriority( SPI_IRQn, INT_PRIORITY_SPI );
      NVIC_EnableIRQ( SPI_IRQn );
  
      /* Initialize SPI link. */
      if( ksz8851snl_init() < 0 )
      {
          FreeRTOS_printf( ( "ksz8851snl_low_level_init: failed to initialize the Micrel driver!\n" ) );
          configASSERT( ipFALSE_BOOL );
      }
  
      memset( xMicrelDevice.pusHashTable, 255, sizeof( xMicrelDevice.pusHashTable ) );
      ksz8851_reg_write( REG_MAC_HASH_0, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 0 ] ) );
      ksz8851_reg_write( REG_MAC_HASH_2, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 1 ] ) );
      ksz8851_reg_write( REG_MAC_HASH_4, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 2 ] ) );
      ksz8851_reg_write( REG_MAC_HASH_6, FreeRTOS_htons( xMicrelDevice.pusHashTable[ 3 ] ) );
  
      /* Initialize interrupt line INTN. */
      configure_intn( INTN_Handler );
  }
  
  /**
   * \brief Use pre-allocated pbuf as DMA source and return the incoming packet.
   *
   * \param netif the lwIP network interface structure for this ethernetif.
   *
   * \return a pbuf filled with the received packet (including MAC header).
   * 0 on memory error.
   */
  static NetworkBufferDescriptor_t * ksz8851snl_low_level_input( void )
  {
      NetworkBufferDescriptor_t * pxNetworkBuffer = NULL;
      int rxTail = xMicrelDevice.us_rx_tail;
  
      /* Check that descriptor is owned by software (ie packet received). */
      if( xMicrelDevice.rx_ready[ rxTail ] != pdFALSE )
      {
          /* Fetch pre-allocated buffer */
          pxNetworkBuffer = xMicrelDevice.rx_buffers[ rxTail ];
  
          /* Remove this pbuf from its descriptor. */
          xMicrelDevice.rx_buffers[ rxTail ] = NULL;
  
          /* Clears rx_ready and sets rx_buffers. */
          ksz8851snl_rx_populate_queue();
  
          if( ++rxTail == MICREL_RX_BUFFERS )
          {
              rxTail = 0;
          }
  
          xMicrelDevice.us_rx_tail = rxTail;
      }
  
      return pxNetworkBuffer;
  }
  /*-----------------------------------------------------------*/
  
  static uint32_t prvEMACRxPoll( void )
  {
      NetworkBufferDescriptor_t * pxNetworkBuffer;
      IPStackEvent_t xRxEvent = { eNetworkRxEvent, NULL };
      uint32_t ulReturnValue = 0;
  
      for( ; ; )
      {
          /* Only for logging. */
          int rxTail = xMicrelDevice.us_rx_tail;
          EthernetHeader_t * pxEthernetHeader;
  
          pxNetworkBuffer = ksz8851snl_low_level_input();
  
          if( pxNetworkBuffer == NULL )
          {
              break;
          }
  
          pxEthernetHeader = ( EthernetHeader_t * ) ( pxNetworkBuffer->pucEthernetBuffer );
  
          if( ( pxEthernetHeader->usFrameType != ipIPv4_FRAME_TYPE ) &&
              ( pxEthernetHeader->usFrameType != ipARP_FRAME_TYPE ) )
          {
              FreeRTOS_printf( ( "Frame type %02X received\n", pxEthernetHeader->usFrameType ) );
          }
  
          ulReturnValue++;
  
          xRxEvent.pvData = ( void * ) pxNetworkBuffer;
  
          /* Send the descriptor to the IP task for processing. */
          if( xSendEventStructToIPTask( &xRxEvent, 100UL ) != pdTRUE )
          {
              vReleaseNetworkBufferAndDescriptor( pxNetworkBuffer );
              iptraceETHERNET_RX_EVENT_LOST();
              FreeRTOS_printf( ( "prvEMACRxPoll: Can not queue return packet!\n" ) );
          }
      }
  
      return ulReturnValue;
  }
  /*-----------------------------------------------------------*/
  
  static void prvEMACHandlerTask( void * pvParameters )
  {
      TimeOut_t xPhyTime;
      TickType_t xPhyRemTime;
      TickType_t xLoggingTime;
      UBaseType_t uxLastMinBufferCount = 0;
      UBaseType_t uxCurrentCount;
      BaseType_t xResult = 0;
      uint32_t xStatus;
      const TickType_t ulMaxBlockTime = pdMS_TO_TICKS( EMAC_MAX_BLOCK_TIME_MS );
  
      #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
          UBaseType_t uxLastMinQueueSpace = 0;
      #endif
  
      /* Remove compiler warnings about unused parameters. */
      ( void ) pvParameters;
  
      configASSERT( xEMACTaskHandle != NULL );
  
      vTaskSetTimeOutState( &xPhyTime );
      xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
      xLoggingTime = xTaskGetTickCount();
  
      for( ; ; )
      {
          uxCurrentCount = uxGetMinimumFreeNetworkBuffers();
  
          if( uxLastMinBufferCount != uxCurrentCount )
          {
              /* The logging produced below may be helpful
               * while tuning +TCP: see how many buffers are in use. */
              uxLastMinBufferCount = uxCurrentCount;
              FreeRTOS_printf( ( "Network buffers: %lu lowest %lu\n",
                                 uxGetNumberOfFreeNetworkBuffers(), uxCurrentCount ) );
          }
  
          #if ( ipconfigCHECK_IP_QUEUE_SPACE != 0 )
              {
                  uxCurrentCount = uxGetMinimumIPQueueSpace();
  
                  if( uxLastMinQueueSpace != uxCurrentCount )
                  {
                      /* The logging produced below may be helpful
                       * while tuning +TCP: see how many buffers are in use. */
                      uxLastMinQueueSpace = uxCurrentCount;
                      FreeRTOS_printf( ( "Queue space: lowest %lu\n", uxCurrentCount ) );
                  }
              }
          #endif /* ipconfigCHECK_IP_QUEUE_SPACE */
  
          /* Run the state-machine of the ksz8851 driver. */
          ksz8851snl_update();
  
          if( ( ulISREvents & EMAC_IF_ALL_EVENT ) == 0 )
          {
              /* No events to process now, wait for the next. */
              ulTaskNotifyTake( pdTRUE, ulMaxBlockTime );
          }
  
          if( ( xTaskGetTickCount() - xLoggingTime ) > 10000 )
          {
              xLoggingTime += 10000;
              FreeRTOS_printf( ( "Now Tx/Rx %7d /%7d\n",
                                 xMicrelDevice.ul_total_tx, xMicrelDevice.ul_total_rx ) );
          }
  
          if( ( ulISREvents & EMAC_IF_RX_EVENT ) != 0 )
          {
              ulISREvents &= ~EMAC_IF_RX_EVENT;
  
              /* Wait for the EMAC interrupt to indicate that another packet has been
               * received. */
              xResult = prvEMACRxPoll();
          }
  
          if( ( ulISREvents & EMAC_IF_TX_EVENT ) != 0 )
          {
              /* Future extension: code to release TX buffers if zero-copy is used. */
              ulISREvents &= ~EMAC_IF_TX_EVENT;
          }
  
          if( ( ulISREvents & EMAC_IF_ERR_EVENT ) != 0 )
          {
              /* Future extension: logging about errors that occurred. */
              ulISREvents &= ~EMAC_IF_ERR_EVENT;
          }
  
          if( xResult > 0 )
          {
              /* As long as packets are being received, assume that
               * the Link Status is high. */
              ulPHYLinkStatus |= BMSR_LINK_STATUS;
  
              /* A packet was received. No need to check for the PHY status now,
               * but set a timer to check it later on. */
              vTaskSetTimeOutState( &xPhyTime );
              xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
              xResult = 0;
          }
          else if( ( xTaskCheckForTimeOut( &xPhyTime, &xPhyRemTime ) != pdFALSE ) &&
                   ( xMicrelDevice.ul_spi_pdc_status == SPI_PDC_IDLE ) )
          {
              /* Check the link status again. */
              xStatus = ulReadMDIO( PHY_REG_01_BMSR );
  
              if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != ( xStatus & BMSR_LINK_STATUS ) )
              {
                  ulPHYLinkStatus = xStatus;
                  FreeRTOS_printf( ( "prvEMACHandlerTask: PHY LS now %d\n", ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 ) );
              }
  
              vTaskSetTimeOutState( &xPhyTime );
  
              if( ( ulPHYLinkStatus & BMSR_LINK_STATUS ) != 0 )
              {
                  xPhyRemTime = pdMS_TO_TICKS( PHY_LS_HIGH_CHECK_TIME_MS );
              }
              else
              {
                  xPhyRemTime = pdMS_TO_TICKS( PHY_LS_LOW_CHECK_TIME_MS );
              }
          }
      }
  }
  /*-----------------------------------------------------------*/
  