+/*\r
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without modification,\r
+ * are permitted provided that the following conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above copyright notice,\r
+ * this list of conditions and the following disclaimer.\r
+ * 2. Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ * 3. The name of the author may not be used to endorse or promote products\r
+ * derived from this software without specific prior written permission.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED\r
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT\r
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\r
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT\r
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\r
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY\r
+ * OF SUCH DAMAGE.\r
+ *\r
+ * This file is part of the lwIP TCP/IP stack.\r
+ *\r
+ * Author: Adam Dunkels <adam@sics.se>\r
+ *\r
+ */\r
+\r
+/* Standard library includes. */\r
+#include <stdio.h>\r
+#include <string.h>\r
+\r
+/* FreeRTOS includes. */\r
+#include "FreeRTOS.h"\r
+#include "task.h"\r
+\r
+xTaskHandle xEthIntTask;\r
+\r
+/* lwIP includes. */\r
+#include "lwip/def.h"\r
+#include "lwip/mem.h"\r
+#include "lwip/pbuf.h"\r
+#include "lwip/sys.h"\r
+#include "lwip/stats.h"\r
+#include "lwip/snmp.h"\r
+#include "netif/etharp.h"\r
+\r
+/* Hardware includes. */\r
+#include "fec.h"\r
+\r
+/* Delay to wait for a DMA buffer to become available if one is not already\r
+available. */\r
+#define netifBUFFER_WAIT_ATTEMPTS 10\r
+#define netifBUFFER_WAIT_DELAY (10 / portTICK_RATE_MS)\r
+\r
+/* Delay between polling the PHY to see if a link has been established. */\r
+#define netifLINK_DELAY ( 500 / portTICK_RATE_MS )\r
+\r
+/* Delay between looking for incoming packets. In ideal world this would be\r
+infinite. */\r
+#define netifBLOCK_TIME_WAITING_FOR_INPUT netifLINK_DELAY\r
+\r
+/* Name for the netif. */\r
+#define IFNAME0 'e'\r
+#define IFNAME1 'n'\r
+\r
+/* Hardware specific. */\r
+#define netifFIRST_FEC_VECTOR 23\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/* The DMA descriptors. This is a char array to allow us to align it correctly. */\r
+static unsigned portCHAR xFECTxDescriptors_unaligned[ ( configNUM_FEC_TX_BUFFERS * sizeof( FECBD ) ) + 16 ];\r
+static unsigned portCHAR xFECRxDescriptors_unaligned[ ( configNUM_FEC_RX_BUFFERS * sizeof( FECBD ) ) + 16 ];\r
+static FECBD *xFECTxDescriptors;\r
+static FECBD *xFECRxDescriptors;\r
+\r
+/* The DMA buffers. These are char arrays to allow them to be alligned correctly. */\r
+static unsigned portCHAR ucFECTxBuffers[ ( configNUM_FEC_TX_BUFFERS * configFEC_BUFFER_SIZE ) + 16 ];\r
+static unsigned portCHAR ucFECRxBuffers[ ( configNUM_FEC_RX_BUFFERS * configFEC_BUFFER_SIZE ) + 16 ];\r
+static unsigned portBASE_TYPE uxNextRxBuffer = 0, uxNextTxBuffer = 0;\r
+\r
+/* Semaphore used by the FEC interrupt handler to wake the handler task. */\r
+static xSemaphoreHandle xFecSemaphore;\r
+\r
+#pragma options align= packed\r
+struct ethernetif\r
+{\r
+ struct eth_addr *ethaddr;\r
+ /* Add whatever per-interface state that is needed here. */\r
+};\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/* Standard lwIP netif handlers. */\r
+static void prvInitialiseFECBuffers( void );\r
+static void low_level_init( struct netif *netif );\r
+static err_t low_level_output(struct netif *netif, struct pbuf *p);\r
+static struct pbuf *low_level_input(struct netif *netif);\r
+static void ethernetif_input( void *pParams );\r
+\r
+/* Functions adapted from Freescale provided code. */\r
+static int fec_mii_write( int phy_addr, int reg_addr, int data );\r
+static int fec_mii_read( int phy_addr, int reg_addr, uint16* data );\r
+static uint8 fec_hash_address( const uint8* addr );\r
+static void fec_set_address( const uint8 *pa );\r
+static void fec_irq_enable( void );\r
+\r
+/*-----------------------------------------------------------*/\r
+\r
+/********************************************************************/\r
+/*\r
+ * Write a value to a PHY's MII register.\r
+ *\r
+ * Parameters:\r
+ * ch FEC channel\r
+ * phy_addr Address of the PHY.\r
+ * reg_addr Address of the register in the PHY.\r
+ * data Data to be written to the PHY register.\r
+ *\r
+ * Return Values:\r
+ * 0 on failure\r
+ * 1 on success.\r
+ *\r
+ * Please refer to your PHY manual for registers and their meanings.\r
+ * mii_write() polls for the FEC's MII interrupt event and clears it. \r
+ * If after a suitable amount of time the event isn't triggered, a \r
+ * value of 0 is returned.\r
+ */\r
+static int fec_mii_write( int phy_addr, int reg_addr, int data )\r
+{\r
+int timeout;\r
+uint32 eimr;\r
+\r
+ /* Clear the MII interrupt bit */\r
+ MCF_FEC_EIR = MCF_FEC_EIR_MII;\r
+\r
+ /* Mask the MII interrupt */\r
+ eimr = MCF_FEC_EIMR;\r
+ MCF_FEC_EIMR &= ~MCF_FEC_EIMR_MII;\r
+\r
+ /* Write to the MII Management Frame Register to kick-off the MII write */\r
+ MCF_FEC_MMFR = MCF_FEC_MMFR_ST_01 | MCF_FEC_MMFR_OP_WRITE | MCF_FEC_MMFR_PA(phy_addr) | MCF_FEC_MMFR_RA(reg_addr) | MCF_FEC_MMFR_TA_10 | MCF_FEC_MMFR_DATA( data );\r
+\r
+ /* Poll for the MII interrupt (interrupt should be masked) */\r
+ for (timeout = 0; timeout < MII_TIMEOUT; timeout++)\r
+ {\r
+ if (MCF_FEC_EIR & MCF_FEC_EIR_MII)\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if( timeout == MII_TIMEOUT )\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ /* Clear the MII interrupt bit */\r
+ MCF_FEC_EIR = MCF_FEC_EIR_MII;\r
+\r
+ /* Restore the EIMR */\r
+ MCF_FEC_EIMR = eimr;\r
+\r
+ return 1;\r
+}\r
+\r
+/********************************************************************/\r
+/*\r
+ * Read a value from a PHY's MII register.\r
+ *\r
+ * Parameters:\r
+ * ch FEC channel\r
+ * phy_addr Address of the PHY.\r
+ * reg_addr Address of the register in the PHY.\r
+ * data Pointer to storage for the Data to be read\r
+ * from the PHY register (passed by reference)\r
+ *\r
+ * Return Values:\r
+ * 0 on failure\r
+ * 1 on success.\r
+ *\r
+ * Please refer to your PHY manual for registers and their meanings.\r
+ * mii_read() polls for the FEC's MII interrupt event and clears it. \r
+ * If after a suitable amount of time the event isn't triggered, a \r
+ * value of 0 is returned.\r
+ */\r
+static int fec_mii_read( int phy_addr, int reg_addr, uint16* data )\r
+{\r
+int timeout;\r
+uint32 eimr;\r
+\r
+ /* Clear the MII interrupt bit */\r
+ MCF_FEC_EIR = MCF_FEC_EIR_MII;\r
+\r
+ /* Mask the MII interrupt */\r
+ eimr = MCF_FEC_EIMR;\r
+ MCF_FEC_EIMR &= ~MCF_FEC_EIMR_MII;\r
+\r
+ /* Write to the MII Management Frame Register to kick-off the MII read */\r
+ MCF_FEC_MMFR = MCF_FEC_MMFR_ST_01 | MCF_FEC_MMFR_OP_READ | MCF_FEC_MMFR_PA(phy_addr) | MCF_FEC_MMFR_RA(reg_addr) | MCF_FEC_MMFR_TA_10;\r
+\r
+ /* Poll for the MII interrupt (interrupt should be masked) */\r
+ for (timeout = 0; timeout < MII_TIMEOUT; timeout++)\r
+ {\r
+ if (MCF_FEC_EIR & MCF_FEC_EIR_MII)\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if(timeout == MII_TIMEOUT)\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ /* Clear the MII interrupt bit */\r
+ MCF_FEC_EIR = MCF_FEC_EIR_MII;\r
+\r
+ /* Restore the EIMR */\r
+ MCF_FEC_EIMR = eimr;\r
+\r
+ *data = (uint16)(MCF_FEC_MMFR & 0x0000FFFF);\r
+\r
+ return 1;\r
+}\r
+\r
+\r
+/********************************************************************/\r
+/*\r
+ * Generate the hash table settings for the given address\r
+ *\r
+ * Parameters:\r
+ * addr 48-bit (6 byte) Address to generate the hash for\r
+ *\r
+ * Return Value:\r
+ * The 6 most significant bits of the 32-bit CRC result\r
+ */\r
+static uint8 fec_hash_address( const uint8* addr )\r
+{\r
+uint32 crc;\r
+uint8 byte;\r
+int i, j;\r
+\r
+ crc = 0xFFFFFFFF;\r
+ for(i=0; i<6; ++i)\r
+ {\r
+ byte = addr[i];\r
+ for(j=0; j<8; ++j)\r
+ {\r
+ if((byte & 0x01)^(crc & 0x01))\r
+ {\r
+ crc >>= 1;\r
+ crc = crc ^ 0xEDB88320;\r
+ }\r
+ else\r
+ {\r
+ crc >>= 1;\r
+ }\r
+\r
+ byte >>= 1;\r
+ }\r
+ }\r
+\r
+ return (uint8)(crc >> 26);\r
+}\r
+\r
+/********************************************************************/\r
+/*\r
+ * Set the Physical (Hardware) Address and the Individual Address\r
+ * Hash in the selected FEC\r
+ *\r
+ * Parameters:\r
+ * ch FEC channel\r
+ * pa Physical (Hardware) Address for the selected FEC\r
+ */\r
+static void fec_set_address( const uint8 *pa )\r
+{\r
+ uint8 crc;\r
+\r
+ /*\r
+ * Set the Physical Address\r
+ */\r
+ MCF_FEC_PALR = (uint32)((pa[0]<<24) | (pa[1]<<16) | (pa[2]<<8) | pa[3]);\r
+ MCF_FEC_PAUR = (uint32)((pa[4]<<24) | (pa[5]<<16));\r
+\r
+ /*\r
+ * Calculate and set the hash for given Physical Address\r
+ * in the Individual Address Hash registers\r
+ */\r
+ crc = fec_hash_address(pa);\r
+ if(crc >= 32)\r
+ {\r
+ MCF_FEC_IAUR |= (uint32)(1 << (crc - 32));\r
+ }\r
+ else\r
+ {\r
+ MCF_FEC_IALR |= (uint32)(1 << crc);\r
+ }\r
+}\r
+\r
+\r
+/********************************************************************/\r
+/*\r
+ * Enable interrupts on the selected FEC\r
+ *\r
+ */\r
+static void fec_irq_enable( void )\r
+{\r
+int fec_vbase;\r
+\r
+#if INTC_LVL_FEC > configMAX_SYSCALL_INTERRUPT_PRIORITY\r
+ #error INTC_LVL_FEC must be less than or equal to configMAX_SYSCALL_INTERRUPT_PRIORITY\r
+#endif\r
+\r
+ fec_vbase = 64 + netifFIRST_FEC_VECTOR;\r
+ \r
+ /* Enable FEC interrupts to the ColdFire core \r
+ * Setup each ICR with a unique interrupt level combination */\r
+ fec_vbase -= 64;\r
+\r
+ /* FEC Rx Frame */\r
+ MCF_INTC0_ICR(fec_vbase+4) = MCF_INTC_ICR_IL(INTC_LVL_FEC);\r
+\r
+ /* FEC Rx Buffer */ \r
+ MCF_INTC0_ICR(fec_vbase+5) = MCF_INTC_ICR_IL(INTC_LVL_FEC);\r
+\r
+ /* FEC FIFO Underrun */ \r
+ MCF_INTC0_ICR(fec_vbase+2) = MCF_INTC_ICR_IL(INTC_LVL_FEC+1);\r
+\r
+ /* FEC Collision Retry Limit */ \r
+ MCF_INTC0_ICR(fec_vbase+3) = MCF_INTC_ICR_IL(INTC_LVL_FEC+1);\r
+\r
+ /* FEC Late Collision */ \r
+ MCF_INTC0_ICR(fec_vbase+7) = MCF_INTC_ICR_IL(INTC_LVL_FEC+1);\r
+\r
+ /* FEC Heartbeat Error */ \r
+ MCF_INTC0_ICR(fec_vbase+8) = MCF_INTC_ICR_IL(INTC_LVL_FEC+1);\r
+\r
+ /* FEC Bus Error */ \r
+ MCF_INTC0_ICR(fec_vbase+10) = MCF_INTC_ICR_IL(INTC_LVL_FEC+1);\r
+\r
+ /* FEC Babbling Transmit */ \r
+ MCF_INTC0_ICR(fec_vbase+11) = MCF_INTC_ICR_IL(INTC_LVL_FEC+1);\r
+\r
+ /* FEC Babbling Receive */ \r
+ MCF_INTC0_ICR(fec_vbase+12) = MCF_INTC_ICR_IL(INTC_LVL_FEC+1);\r
+ \r
+ /* Enable the FEC interrupts in the mask register */ \r
+ MCF_INTC0_IMRH &= ~( MCF_INTC_IMRH_INT_MASK33 | MCF_INTC_IMRH_INT_MASK34 | MCF_INTC_IMRH_INT_MASK35 );\r
+ MCF_INTC0_IMRL &= ~( MCF_INTC_IMRL_INT_MASK25 | MCF_INTC_IMRL_INT_MASK26 | MCF_INTC_IMRL_INT_MASK27 | MCF_INTC_IMRL_INT_MASK28 | MCF_INTC_IMRL_INT_MASK29 | MCF_INTC_IMRL_INT_MASK30 | MCF_INTC_IMRL_INT_MASK31 | MCF_INTC_IMRL_MASKALL );\r
+\r
+ /* Clear any pending FEC interrupt events */\r
+ MCF_FEC_EIR = MCF_FEC_EIR_CLEAR_ALL;\r
+\r
+ /* Unmask all FEC interrupts */\r
+ MCF_FEC_EIMR = MCF_FEC_EIMR_UNMASK_ALL;\r
+}\r
+\r
+/**\r
+ * In this function, the hardware should be initialized.\r
+ * Called from ethernetif_init().\r
+ *\r
+ * @param netif the already initialized lwip network interface structure\r
+ * for this ethernetif\r
+ */\r
+static void low_level_init( struct netif *netif )\r
+{\r
+unsigned portSHORT usData;\r
+const unsigned portCHAR ucMACAddress[6] = \r
+{\r
+ configMAC_0, configMAC_1,configMAC_2,configMAC_3,configMAC_4,configMAC_5\r
+};\r
+\r
+ prvInitialiseFECBuffers();\r
+ vSemaphoreCreateBinary( xFecSemaphore );\r
+ \r
+ for( usData = 0; usData < 6; usData++ )\r
+ {\r
+ netif->hwaddr[ usData ] = ucMACAddress[ usData ];\r
+ }\r
+\r
+ /* Set the Reset bit and clear the Enable bit */\r
+ MCF_FEC_ECR = MCF_FEC_ECR_RESET;\r
+\r
+ /* Wait at least 8 clock cycles */\r
+ for( usData = 0; usData < 10; usData++ )\r
+ {\r
+ asm( "NOP" );\r
+ }\r
+\r
+ /* Set MII speed to 2.5MHz. */\r
+ MCF_FEC_MSCR = MCF_FEC_MSCR_MII_SPEED( ( ( configCPU_CLOCK_HZ / 1000000 ) / 5 ) + 1 );\r
+\r
+ /*\r
+ * Make sure the external interface signals are enabled\r
+ */\r
+ MCF_GPIO_PNQPAR = MCF_GPIO_PNQPAR_IRQ3_FEC_MDIO | MCF_GPIO_PNQPAR_IRQ5_FEC_MDC;\r
+\r
+\r
+ MCF_GPIO_PTIPAR = MCF_GPIO_PTIPAR_FEC_COL_FEC_COL \r
+ | MCF_GPIO_PTIPAR_FEC_CRS_FEC_CRS \r
+ | MCF_GPIO_PTIPAR_FEC_RXCLK_FEC_RXCLK \r
+ | MCF_GPIO_PTIPAR_FEC_RXD0_FEC_RXD0 \r
+ | MCF_GPIO_PTIPAR_FEC_RXD1_FEC_RXD1 \r
+ | MCF_GPIO_PTIPAR_FEC_RXD2_FEC_RXD2 \r
+ | MCF_GPIO_PTIPAR_FEC_RXD3_FEC_RXD3 \r
+ | MCF_GPIO_PTIPAR_FEC_RXDV_FEC_RXDV; \r
+\r
+ MCF_GPIO_PTJPAR = MCF_GPIO_PTJPAR_FEC_RXER_FEC_RXER \r
+ | MCF_GPIO_PTJPAR_FEC_TXCLK_FEC_TXCLK \r
+ | MCF_GPIO_PTJPAR_FEC_TXD0_FEC_TXD0 \r
+ | MCF_GPIO_PTJPAR_FEC_TXD1_FEC_TXD1 \r
+ | MCF_GPIO_PTJPAR_FEC_TXD2_FEC_TXD2 \r
+ | MCF_GPIO_PTJPAR_FEC_TXD3_FEC_TXD3 \r
+ | MCF_GPIO_PTJPAR_FEC_TXEN_FEC_TXEN \r
+ | MCF_GPIO_PTJPAR_FEC_TXER_FEC_TXER; \r
+\r
+\r
+ /* Can we talk to the PHY? */\r
+ do\r
+ {\r
+ vTaskDelay( netifLINK_DELAY );\r
+ usData = 0;\r
+ fec_mii_read( configPHY_ADDRESS, PHY_PHYIDR1, &usData );\r
+\r
+ } while( ( usData == 0xffff ) || ( usData == 0 ) );\r
+\r
+ /* Start auto negotiate. */\r
+ fec_mii_write( configPHY_ADDRESS, PHY_BMCR, ( PHY_BMCR_AN_RESTART | PHY_BMCR_AN_ENABLE ) );\r
+\r
+ /* Wait for auto negotiate to complete. */\r
+ do\r
+ {\r
+ vTaskDelay( netifLINK_DELAY );\r
+ fec_mii_read( configPHY_ADDRESS, PHY_BMSR, &usData );\r
+\r
+ } while( !( usData & PHY_BMSR_AN_COMPLETE ) );\r
+\r
+ /* When we get here we have a link - find out what has been negotiated. */\r
+ fec_mii_read( configPHY_ADDRESS, PHY_ANLPAR, &usData );\r
+ \r
+ if( ( usData & PHY_ANLPAR_100BTX_FDX ) || ( usData & PHY_ANLPAR_100BTX ) )\r
+ {\r
+ /* Speed is 100. */\r
+ }\r
+ else\r
+ {\r
+ /* Speed is 10. */\r
+ }\r
+\r
+ if( ( usData & PHY_ANLPAR_100BTX_FDX ) || ( usData & PHY_ANLPAR_10BTX_FDX ) )\r
+ {\r
+ /* Full duplex. */\r
+ MCF_FEC_RCR &= (uint32)~MCF_FEC_RCR_DRT;\r
+ MCF_FEC_TCR |= MCF_FEC_TCR_FDEN;\r
+ }\r
+ else\r
+ {\r
+ MCF_FEC_RCR |= MCF_FEC_RCR_DRT;\r
+ MCF_FEC_TCR &= (uint32)~MCF_FEC_TCR_FDEN;\r
+ }\r
+ \r
+ /* Clear the Individual and Group Address Hash registers */\r
+ MCF_FEC_IALR = 0;\r
+ MCF_FEC_IAUR = 0;\r
+ MCF_FEC_GALR = 0;\r
+ MCF_FEC_GAUR = 0;\r
+\r
+ /* Set the Physical Address for the selected FEC */\r
+ fec_set_address( ucMACAddress );\r
+\r
+ /* Set Rx Buffer Size */\r
+ MCF_FEC_EMRBR = (uint16)configFEC_BUFFER_SIZE;\r
+\r
+ /* Point to the start of the circular Rx buffer descriptor queue */\r
+ MCF_FEC_ERDSR = ( volatile unsigned portLONG ) &( xFECRxDescriptors[ 0 ] );\r
+\r
+ /* Point to the start of the circular Tx buffer descriptor queue */\r
+ MCF_FEC_ETSDR = ( volatile unsigned portLONG ) &( xFECTxDescriptors[ 0 ] );\r
+\r
+ /* Mask all FEC interrupts */\r
+ MCF_FEC_EIMR = MCF_FEC_EIMR_MASK_ALL;\r
+\r
+ /* Clear all FEC interrupt events */\r
+ MCF_FEC_EIR = MCF_FEC_EIR_CLEAR_ALL;\r
+\r
+ /* Initialize the Receive Control Register */\r
+ MCF_FEC_RCR = MCF_FEC_RCR_MAX_FL(ETH_MAX_FRM) | MCF_FEC_RCR_FCE;\r
+\r
+ MCF_FEC_RCR |= MCF_FEC_RCR_MII_MODE;\r
+\r
+ #if( configUSE_PROMISCUOUS_MODE == 1 )\r
+ {\r
+ MCF_FEC_RCR |= MCF_FEC_RCR_PROM;\r
+ }\r
+ #endif\r
+\r
+ /* Create the task that handles the EMAC. */\r
+ xTaskCreate( ethernetif_input, ( signed portCHAR * ) "ETH_INT", configETHERNET_INPUT_TASK_STACK_SIZE, (void *)netif, configETHERNET_INPUT_TASK_PRIORITY, &xEthIntTask );\r
+ \r
+ fec_irq_enable();\r
+ MCF_FEC_ECR = MCF_FEC_ECR_ETHER_EN;\r
+ MCF_FEC_RDAR = MCF_FEC_RDAR_R_DES_ACTIVE; \r
+}\r
+\r
+/**\r
+ * This function should do the actual transmission of the packet. The packet is\r
+ * contained in the pbuf that is passed to the function. This pbuf\r
+ * might be chained.\r
+ *\r
+ * @param netif the lwip network interface structure for this ethernetif\r
+ * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)\r
+ * @return ERR_OK if the packet could be sent\r
+ * an err_t value if the packet couldn't be sent\r
+ *\r
+ * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to\r
+ * strange results. You might consider waiting for space in the DMA queue\r
+ * to become availale since the stack doesn't retry to send a packet\r
+ * dropped because of memory failure (except for the TCP timers).\r
+ */\r
+static err_t low_level_output(struct netif *netif, struct pbuf *p)\r
+{\r
+struct pbuf *q;\r
+u32_t l = 0;\r
+unsigned portCHAR *pcTxData = NULL;\r
+portBASE_TYPE i;\r
+\r
+ ( void ) netif;\r
+\r
+ #if ETH_PAD_SIZE\r
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */\r
+ #endif\r
+\r
+ /* Get a DMA buffer into which we can write the data to send. */\r
+ for( i = 0; i < netifBUFFER_WAIT_ATTEMPTS; i++ )\r
+ {\r
+ if( xFECTxDescriptors[ uxNextTxBuffer ].status & TX_BD_R )\r
+ {\r
+ /* Wait for the buffer to become available. */\r
+ vTaskDelay( netifBUFFER_WAIT_DELAY );\r
+ }\r
+ else\r
+ {\r
+ pcTxData = xFECTxDescriptors[ uxNextTxBuffer ].data;\r
+ break;\r
+ }\r
+ }\r
+\r
+ if( pcTxData == NULL ) \r
+ {\r
+ /* For break point only. */\r
+ portNOP();\r
+\r
+ return ERR_BUF;\r
+ }\r
+ else \r
+ {\r
+ for( q = p; q != NULL; q = q->next ) \r
+ {\r
+ /* Send the data from the pbuf to the interface, one pbuf at a\r
+ time. The size of the data in each pbuf is kept in the ->len\r
+ variable. */\r
+ memcpy( &pcTxData[l], (u8_t*)q->payload, q->len );\r
+ l += q->len;\r
+ }\r
+ }\r
+\r
+ /* Setup the buffer descriptor for transmission */\r
+ xFECTxDescriptors[ uxNextTxBuffer ].length = l;//nbuf->length + ETH_HDR_LEN;\r
+ xFECTxDescriptors[ uxNextTxBuffer ].status |= (TX_BD_R | TX_BD_L);\r
+\r
+ /* Continue the Tx DMA task (in case it was waiting for a new TxBD) */\r
+ MCF_FEC_TDAR = MCF_FEC_TDAR_X_DES_ACTIVE;\r
+\r
+ uxNextTxBuffer++;\r
+ if( uxNextTxBuffer >= configNUM_FEC_TX_BUFFERS )\r
+ {\r
+ uxNextTxBuffer = 0;\r
+ }\r
+\r
+ #if ETH_PAD_SIZE\r
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */\r
+ #endif\r
+\r
+ LINK_STATS_INC(link.xmit);\r
+\r
+ return ERR_OK;\r
+}\r
+\r
+/**\r
+ * Should allocate a pbuf and transfer the bytes of the incoming\r
+ * packet from the interface into the pbuf.\r
+ *\r
+ * @param netif the lwip network interface structure for this ethernetif\r
+ * @return a pbuf filled with the received packet (including MAC header)\r
+ * NULL on memory error\r
+ */\r
+static struct pbuf *low_level_input(struct netif *netif)\r
+{\r
+struct pbuf *p, *q;\r
+u16_t len, l;\r
+\r
+ ( void ) netif;\r
+\r
+ l = 0;\r
+ p = NULL;\r
+\r
+ /* Obtain the size of the packet and put it into the "len" variable. */\r
+ len = xFECRxDescriptors[ uxNextRxBuffer ].length;\r
+\r
+ if( ( len != 0 ) && ( ( xFECRxDescriptors[ uxNextRxBuffer ].status & RX_BD_E ) == 0 ) )\r
+ {\r
+ #if ETH_PAD_SIZE\r
+ len += ETH_PAD_SIZE; /* allow room for Ethernet padding */\r
+ #endif\r
+\r
+ /* We allocate a pbuf chain of pbufs from the pool. */\r
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);\r
+\r
+ if (p != NULL) \r
+ {\r
+\r
+ #if ETH_PAD_SIZE\r
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */\r
+ #endif\r
+\r
+ /* We iterate over the pbuf chain until we have read the entire\r
+ * packet into the pbuf. */\r
+ for(q = p; q != NULL; q = q->next) \r
+ {\r
+ /* Read enough bytes to fill this pbuf in the chain. The\r
+ * available data in the pbuf is given by the q->len\r
+ * variable. */\r
+ memcpy((u8_t*)q->payload, &(xFECRxDescriptors[ uxNextRxBuffer ].data[l]), q->len);\r
+ l = l + q->len;\r
+ }\r
+\r
+\r
+ #if ETH_PAD_SIZE\r
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */\r
+ #endif\r
+\r
+ LINK_STATS_INC(link.recv);\r
+\r
+ } \r
+ else \r
+ {\r
+\r
+ LINK_STATS_INC(link.memerr);\r
+ LINK_STATS_INC(link.drop);\r
+\r
+ } /* End else */\r
+ \r
+ \r
+ /* Free the descriptor. */\r
+ xFECRxDescriptors[ uxNextRxBuffer ].status |= RX_BD_E;\r
+ MCF_FEC_RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;\r
+ \r
+ uxNextRxBuffer++;\r
+ if( uxNextRxBuffer >= configNUM_FEC_RX_BUFFERS )\r
+ {\r
+ uxNextRxBuffer = 0;\r
+ } \r
+ \r
+ } /* End if */\r
+\r
+ return p;\r
+}\r
+\r
+/**\r
+ * This function should be called when a packet is ready to be read\r
+ * from the interface. It uses the function low_level_input() that\r
+ * should handle the actual reception of bytes from the network\r
+ * interface.Then the type of the received packet is determined and\r
+ * the appropriate input function is called.\r
+ *\r
+ * @param netif the lwip network interface structure for this ethernetif\r
+ */\r
+\r
+static void ethernetif_input( void *pParams )\r
+{\r
+struct netif *netif;\r
+struct ethernetif *ethernetif;\r
+struct eth_hdr *ethhdr;\r
+struct pbuf *p;\r
+\r
+ netif = (struct netif*) pParams;\r
+ ethernetif = netif->state;\r
+\r
+ for( ;; )\r
+ {\r
+ do\r
+ {\r
+\r
+ /* move received packet into a new pbuf */\r
+ p = low_level_input( netif );\r
+\r
+ if( p == NULL )\r
+ {\r
+ /* No packet could be read. Wait a for an interrupt to tell us\r
+ there is more data available. */\r
+ xSemaphoreTake( xFecSemaphore, netifBLOCK_TIME_WAITING_FOR_INPUT );\r
+ }\r
+\r
+ } while( p == NULL );\r
+\r
+ /* points to packet payload, which starts with an Ethernet header */\r
+ ethhdr = p->payload;\r
+\r
+ switch (htons(ethhdr->type)) {\r
+ /* IP or ARP packet? */\r
+\r
+ case ETHTYPE_IP:\r
+\r
+ pbuf_header( p, (s16_t)-sizeof(struct eth_hdr) );\r
+\r
+ /* full packet send to tcpip_thread to process */\r
+ if (netif->input(p, netif) != ERR_OK)\r
+ {\r
+ LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));\r
+ pbuf_free(p);\r
+ p = NULL;\r
+ }\r
+ break;\r
+\r
+ case ETHTYPE_ARP:\r
+\r
+ #if ETHARP_TRUST_IP_MAC\r
+ etharp_ip_input(netif, p);\r
+ #endif\r
+\r
+ etharp_arp_input(netif, ethernetif->ethaddr, p);\r
+ break;\r
+\r
+ default:\r
+ pbuf_free(p);\r
+ p = NULL;\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * Should be called at the beginning of the program to set up the\r
+ * network interface. It calls the function low_level_init() to do the\r
+ * actual setup of the hardware.\r
+ *\r
+ * This function should be passed as a parameter to netif_add().\r
+ *\r
+ * @param netif the lwip network interface structure for this ethernetif\r
+ * @return ERR_OK if the loopif is initialized\r
+ * ERR_MEM if private data couldn't be allocated\r
+ * any other err_t on error\r
+ */\r
+err_t ethernetif_init(struct netif *netif)\r
+{\r
+ struct ethernetif *ethernetif;\r
+\r
+ LWIP_ASSERT("netif != NULL", (netif != NULL));\r
+\r
+ ethernetif = mem_malloc(sizeof(struct ethernetif));\r
+\r
+ if (ethernetif == NULL)\r
+ {\r
+ LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));\r
+ return ERR_MEM;\r
+ }\r
+\r
+ #if LWIP_NETIF_HOSTNAME\r
+ /* Initialize interface hostname */\r
+ netif->hostname = "lwip";\r
+ #endif /* LWIP_NETIF_HOSTNAME */\r
+\r
+ /*\r
+ * Initialize the snmp variables and counters inside the struct netif.\r
+ * The last argument should be replaced with your link speed, in units\r
+ * of bits per second.\r
+ */\r
+ NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 100);\r
+\r
+ netif->state = ethernetif;\r
+ netif->name[0] = IFNAME0;\r
+ netif->name[1] = IFNAME1;\r
+\r
+ /* We directly use etharp_output() here to save a function call.\r
+ * You can instead declare your own function an call etharp_output()\r
+ * from it if you have to do some checks before sending (e.g. if link\r
+ * is available...)\r
+ */\r
+ netif->output = etharp_output;\r
+ netif->linkoutput = low_level_output;\r
+\r
+ ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);\r
+\r
+ low_level_init(netif);\r
+\r
+ return ERR_OK;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvInitialiseFECBuffers( void )\r
+{\r
+unsigned portBASE_TYPE ux;\r
+unsigned portCHAR *pcBufPointer;\r
+\r
+ pcBufPointer = &( xFECTxDescriptors_unaligned[ 0 ] );\r
+ while( ( ( unsigned portLONG ) pcBufPointer & 0x0fUL ) != 0 )\r
+ {\r
+ pcBufPointer++;\r
+ }\r
+ \r
+ xFECTxDescriptors = ( FECBD * ) pcBufPointer;\r
+ \r
+ pcBufPointer = &( xFECRxDescriptors_unaligned[ 0 ] );\r
+ while( ( ( unsigned portLONG ) pcBufPointer & 0x0fUL ) != 0 )\r
+ {\r
+ pcBufPointer++;\r
+ }\r
+ \r
+ xFECRxDescriptors = ( FECBD * ) pcBufPointer;\r
+\r
+\r
+ /* Setup the buffers and descriptors. */\r
+ pcBufPointer = &( ucFECTxBuffers[ 0 ] );\r
+ while( ( ( unsigned portLONG ) pcBufPointer & 0x0fUL ) != 0 )\r
+ {\r
+ pcBufPointer++;\r
+ }\r
+\r
+ for( ux = 0; ux < configNUM_FEC_TX_BUFFERS; ux++ )\r
+ {\r
+ xFECTxDescriptors[ ux ].status = TX_BD_TC;\r
+ xFECTxDescriptors[ ux ].data = pcBufPointer;\r
+ pcBufPointer += configFEC_BUFFER_SIZE;\r
+ xFECTxDescriptors[ ux ].length = 0;\r
+ }\r
+\r
+ pcBufPointer = &( ucFECRxBuffers[ 0 ] );\r
+ while( ( ( unsigned portLONG ) pcBufPointer & 0x0fUL ) != 0 )\r
+ {\r
+ pcBufPointer++;\r
+ }\r
+ \r
+ for( ux = 0; ux < configNUM_FEC_RX_BUFFERS; ux++ )\r
+ {\r
+ xFECRxDescriptors[ ux ].status = RX_BD_E;\r
+ xFECRxDescriptors[ ux ].length = configFEC_BUFFER_SIZE;\r
+ xFECRxDescriptors[ ux ].data = pcBufPointer;\r
+ pcBufPointer += configFEC_BUFFER_SIZE;\r
+ }\r
+\r
+ /* Set the wrap bit in the last descriptors to form a ring. */\r
+ xFECTxDescriptors[ configNUM_FEC_TX_BUFFERS - 1 ].status |= TX_BD_W;\r
+ xFECRxDescriptors[ configNUM_FEC_RX_BUFFERS - 1 ].status |= RX_BD_W;\r
+\r
+ uxNextRxBuffer = 0;\r
+ uxNextTxBuffer = 0;\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+__declspec(interrupt:0) void vFECISRHandler( void )\r
+{\r
+unsigned portLONG ulEvent;\r
+portBASE_TYPE xHighPriorityTaskWoken = pdFALSE;\r
+ \r
+ ulEvent = MCF_FEC_EIR & MCF_FEC_EIMR;\r
+ MCF_FEC_EIR = ulEvent;\r
+\r
+ if( ( ulEvent & MCF_FEC_EIR_RXB ) || ( ulEvent & MCF_FEC_EIR_RXF ) )\r
+ {\r
+ /* A packet has been received. Wake the handler task. */\r
+ xSemaphoreGiveFromISR( xFecSemaphore, &xHighPriorityTaskWoken );\r
+ }\r
+\r
+ if (ulEvent & ( MCF_FEC_EIR_UN | MCF_FEC_EIR_RL | MCF_FEC_EIR_LC | MCF_FEC_EIR_EBERR | MCF_FEC_EIR_BABT | MCF_FEC_EIR_BABR | MCF_FEC_EIR_HBERR ) )\r
+ {\r
+ /* Sledge hammer error handling. */\r
+ prvInitialiseFECBuffers();\r
+ MCF_FEC_RDAR = MCF_FEC_RDAR_R_DES_ACTIVE;\r
+ }\r
+\r
+ portEND_SWITCHING_ISR( xHighPriorityTaskWoken );\r
+}\r