//  [c]
error_t ksz8851Init(NetInterface *interface)
{
   Ksz8851Context *context;

   //Point to the driver context
   context = (Ksz8851Context *) interface->nicContext;

   //Debug message
   TRACE_INFO("Initializing KSZ8851 Ethernet controller...\r\n");

#if (KSZ8851_SPI_SUPPORT == ENABLED)
   //Initialize SPI
   interface->spiDriver->init();
#endif

   //Initialize external interrupt line
   interface->extIntDriver->init();

   //Debug message
   TRACE_DEBUG("CIDER=0x%04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_CIDER));
   TRACE_DEBUG("PHY1ILR=0x%04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_PHY1ILR));
   TRACE_DEBUG("PHY1IHR=0x%04" PRIX16 "\r\n", ksz8851ReadReg(interface, KSZ8851_PHY1IHR));

   //Check device ID and revision ID
   if(ksz8851ReadReg(interface, KSZ8851_CIDER) != (KSZ8851_CIDER_FAMILY_ID_DEFAULT |
      KSZ8851_CIDER_CHIP_ID_DEFAULT | KSZ8851_CIDER_REV_ID_A3))
   {
      return ERROR_WRONG_IDENTIFIER;
   }

   //Dump registers for debugging purpose
   ksz8851DumpReg(interface);

   //Initialize driver specific variables
   context->frameId = 0;

   //Allocate TX and RX buffers
   context->txBuffer = memPoolAlloc(ETH_MAX_FRAME_SIZE);
   context->rxBuffer = memPoolAlloc(ETH_MAX_FRAME_SIZE);

   //Failed to allocate memory?
   if(context->txBuffer == NULL || context->rxBuffer == NULL)
   {
      //Clean up side effects
      memPoolFree(context->txBuffer);
      memPoolFree(context->rxBuffer);

      //Report an error
      return ERROR_OUT_OF_MEMORY;
   }

   //Initialize MAC address
   ksz8851WriteReg(interface, KSZ8851_MARH, htons(interface->macAddr.w[0]));
   ksz8851WriteReg(interface, KSZ8851_MARM, htons(interface->macAddr.w[1]));
   ksz8851WriteReg(interface, KSZ8851_MARL, htons(interface->macAddr.w[2]));

   //Packets shorter than 64 bytes are padded and the CRC is automatically
   //generated
   ksz8851WriteReg(interface, KSZ8851_TXCR, KSZ8851_TXCR_TXFCE |
      KSZ8851_TXCR_TXPE | KSZ8851_TXCR_TXCE);

   //Automatically increment TX data pointer
   ksz8851WriteReg(interface, KSZ8851_TXFDPR, KSZ8851_TXFDPR_TXFPAI);

   //Configure address filtering
   ksz8851WriteReg(interface, KSZ8851_RXCR1, KSZ8851_RXCR1_RXPAFMA |
      KSZ8851_RXCR1_RXFCE | KSZ8851_RXCR1_RXBE | KSZ8851_RXCR1_RXME |
      KSZ8851_RXCR1_RXUE);

   //No checksum verification
   ksz8851WriteReg(interface, KSZ8851_RXCR2, KSZ8851_RXCR2_SRDBL_SINGLE_FRAME |
      KSZ8851_RXCR2_IUFFP | KSZ8851_RXCR2_RXIUFCEZ);

   //Enable automatic RXQ frame buffer dequeue
   ksz8851WriteReg(interface, KSZ8851_RXQCR, KSZ8851_RXQCR_RXFCTE |
      KSZ8851_RXQCR_ADRFE);

   //Automatically increment RX data pointer
   ksz8851WriteReg(interface, KSZ8851_RXFDPR, KSZ8851_RXFDPR_RXFPAI);
   //Configure receive frame count threshold
   ksz8851WriteReg(interface, KSZ8851_RXFCTR, 1);

   //Force link in half-duplex if auto-negotiation failed
   ksz8851ClearBit(interface, KSZ8851_P1CR, KSZ8851_P1CR_FORCE_DUPLEX);
   //Restart auto-negotiation
   ksz8851SetBit(interface, KSZ8851_P1CR, KSZ8851_P1CR_RESTART_AN);

   //Clear interrupt flags
   ksz8851SetBit(interface, KSZ8851_ISR, KSZ8851_ISR_LCIS |
      KSZ8851_ISR_TXIS | KSZ8851_ISR_RXIS | KSZ8851_ISR_RXOIS |
      KSZ8851_ISR_TXPSIS | KSZ8851_ISR_RXPSIS | KSZ8851_ISR_TXSAIS |
      KSZ8851_ISR_RXWFDIS | KSZ8851_ISR_RXMPDIS | KSZ8851_ISR_LDIS |
      KSZ8851_ISR_EDIS | KSZ8851_ISR_SPIBEIS);

   //Configure interrupts as desired
   ksz8851SetBit(interface, KSZ8851_IER, KSZ8851_IER_LCIE |
      KSZ8851_IER_TXIE | KSZ8851_IER_RXIE);

   //Enable TX operation
   ksz8851SetBit(interface, KSZ8851_TXCR, KSZ8851_TXCR_TXE);
   //Enable RX operation
   ksz8851SetBit(interface, KSZ8851_RXCR1, KSZ8851_RXCR1_RXE);

   //Accept any packets from the upper layer
   osSetEvent(&interface->nicTxEvent);

   //Force the TCP/IP stack to poll the link state at startup
   interface->nicEvent = TRUE;
   //Notify the TCP/IP stack of the event
   osSetEvent(&netEvent);

   //Successful initialization
   return NO_ERROR;
}