/* * lwIP Ethernet CMSIS-compliant driver for Stellaris MCUs * Copyright (c) 2010 Quantum Leaps, LLC, www.state-machine.com */ /* * Copyright (c) 2001-2004 Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This file is part of the lwIP TCP/IP stack. * * Author: Adam Dunkels */ /* * Copyright (c) 2008 Luminary Micro, Inc. * * This file is dervied from the ``ethernetif.c'' skeleton Ethernet network * interface driver for lwIP. */ #include "qp_port.h" #include "lwip/opt.h" #include "lwip/init.h" #include "lwip/def.h" #include "lwip/mem.h" #include "lwip/pbuf.h" #include "lwip/sys.h" #include "lwip/stats.h" #include "lwip/snmp.h" #include "lwip/dhcp.h" #include "lwip/autoip.h" #include "netif/etharp.h" #include "netif/eth_driver.h" #include "netif/lpc17xx_emac.h" #include "netif/emac.h" //#include "lm3s_cmsis.h" #include /* for memcpy() */ /** * Sanity Check: This interface driver will NOT work if the following defines * are incorrect. */ //#if (PBUF_LINK_HLEN != 16) //#error "PBUF_LINK_HLEN must be 16 for this interface driver!" //#endif //#if (ETH_PAD_SIZE != 2) //#error "ETH_PAD_SIZE must be 2 for this interface driver!" //#endif /** * Setup processing for PTP (IEEE-1588). */ #if LWIP_PTPD void lwIPHostGetTime(u32_t *time_s, u32_t *time_ns); #endif #ifndef TX_PBUF_QUEUE_LEN #define TX_PBUF_QUEUE_LEN 8 #endif /* Helper queue of pbufs */ typedef struct PbufQueueTag { struct pbuf *ring[TX_PBUF_QUEUE_LEN]; uint8_t qwrite; uint8_t qread; uint8_t overflow; } PbufQueue; static void PbufQueue_ctor(PbufQueue *me); static uint8_t PbufQueue_put(PbufQueue *me, struct pbuf *p); static struct pbuf *PbufQueue_get(PbufQueue *me); #define PbufQueue_isEmpty(me_) ((me_)->qwrite == (me_)->qread) /*..........................................................................*/ static struct netif l_netif; /* the single network interface */ static QActive *l_active; /* active object associated with this driver */ static PbufQueue l_txq; /* queue of pbufs for transmission */ static err_t ethernetif_init(struct netif *netif); static err_t ethernetif_output(struct netif *netif, struct pbuf *p); static struct pbuf *low_level_receive(void); static void low_level_transmit(struct pbuf *p); /*..........................................................................*/ struct netif *eth_driver_init(QActive *active, u8_t macaddr[NETIF_MAX_HWADDR_LEN]) { struct ip_addr ipaddr; struct ip_addr netmask; struct ip_addr gw; lwip_init(); /* nitialize the lwIP stack */ /* set MAC address in the network interface... */ l_netif.hwaddr_len = NETIF_MAX_HWADDR_LEN; memcpy(&l_netif.hwaddr[0], macaddr, NETIF_MAX_HWADDR_LEN); l_active = active; /*save the active object associated with this driver */ #if LWIP_NETIF_HOSTNAME l_netif.hostname = "lwIP"; /* initialize interface hostname */ #endif l_netif.name[0] = 'Q'; l_netif.name[1] = 'P'; /* * Initialize the snmp variables and counters inside the struct netif. * The last argument should be replaced with your link speed, in units * of bits per second. */ NETIF_INIT_SNMP(&l_netif, snmp_ifType_ethernet_csmacd, 1000000); /* We directly use etharp_output() here to save a function call. * You can instead declare your own function an call etharp_output() * from it if you have to do some checks before sending (e.g. if link * is available...) */ l_netif.output = ðarp_output; l_netif.linkoutput = ðernetif_output; PbufQueue_ctor(&l_txq); /* initialize the TX pbuf queue */ #if (LWIP_DHCP == 0) && (LWIP_AUTOIP == 0) /* No mechanism of obtaining IP address specified, use static IP: */ IP4_ADDR(&ipaddr, STATIC_IPADDR0, STATIC_IPADDR1, STATIC_IPADDR2, STATIC_IPADDR3); IP4_ADDR(&netmask, STATIC_NET_MASK0, STATIC_NET_MASK1, STATIC_NET_MASK2, STATIC_NET_MASK3); IP4_ADDR(&gw, STATIC_GW_IPADDR0, STATIC_GW_IPADDR1, STATIC_GW_IPADDR2, STATIC_GW_IPADDR3); #else /* either DHCP or AUTOIP are configured, start with zero IP addresses: */ IP4_ADDR(&ipaddr, 0, 0, 0, 0); IP4_ADDR(&netmask, 0, 0, 0, 0); IP4_ADDR(&gw, 0, 0, 0, 0); #endif /* add and configure the Ethernet interface with default settings */ netif_add(&l_netif, &ipaddr, &netmask, &gw, /* configured IP addresses */ active, /* use this active object as the state */ ðernetif_init, /* Ethernet interface initialization */ &ip_input); /* standard IP input processing */ netif_set_default(&l_netif); netif_set_up(&l_netif); /* bring the interface up */ #if (LWIP_DHCP != 0) dhcp_start(&l_netif); /* start DHCP if configured in lwipopts.h */ /* NOTE: If LWIP_AUTOIP is configured in lwipopts.h and * LWIP_DHCP_AUTOIP_COOP is set as well, the DHCP process will start * AutoIP after DHCP fails for 59 seconds. */ #elif (LWIP_AUTOIP != 0) autoip_start(&l_netif); /* start AutoIP if configured in lwipopts.h */ #endif return &l_netif; } /*..........................................................................*/ void eth_driver_read(void) { struct pbuf *p = low_level_receive(); if (p != NULL) { /* new packet received into the pbuf? */ if (ethernet_input(p, &l_netif) != ERR_OK) { /* pbuf not handled? */ LWIP_DEBUGF(NETIF_DEBUG, ("eth_driver_input: input error\n")); pbuf_free(p); /* free the pbuf */ } /* try to output a packet if TX fifo is empty and pbuf is available */ //if ((ETH->TR & MAC_TR_NEWTX) == 0) { if(( LPC_EMAC->Status & EMAC_SR_TX_EN) == 0) { p = PbufQueue_get(&l_txq); if (p != NULL) { low_level_transmit(p); pbuf_free(p); /* free the pbuf, lwIP knows nothing of it */ } } } //ETH->IM |= ETH_INT_RX; /* re-enable the RX interrupt */ LPC_EMAC->IntEnable |= EMAC_INT_RX_DONE; } /*..........................................................................*/ void eth_driver_write(void) { //if ((ETH->TR & MAC_TR_NEWTX) == 0) { if(( LPC_EMAC->Status & EMAC_SR_TX_EN) == 0) { /* TX fifo empty? */ struct pbuf *p = PbufQueue_get(&l_txq); if (p != NULL) { low_level_transmit(p); pbuf_free(p); /* free the pbuf, lwIP knows nothing of it */ } } } /*..........................................................................*/ /* * This function will either write the pbuf into the Stellaris TX FIFO, * or will put the packet in the TX queue of pbufs for subsequent * transmission when the transmitter becomes idle. * * @param netif the lwip network interface structure for this ethernetif * @param p the pbuf to send * @return ERR_OK if the packet could be sent * an err_t value if the packet couldn't be sent * */ static err_t ethernetif_output(struct netif *netif, struct pbuf *p) { if (PbufQueue_isEmpty(&l_txq) && /* nothing in the TX queue? */ ((LPC_EMAC->Status & EMAC_SR_TX_EN) == 0)) // ((ETH->TR & MAC_TR_NEWTX) == 0) /* TX empty? */ { low_level_transmit(p); /* send the pbuf right away */ /* the pbuf will be freed by the lwIP code */ } else { /* otherwise post the pbuf to the transmit queue */ if (PbufQueue_put(&l_txq, p)) { /*could the TX queue take the pbuf? */ pbuf_ref(p); /* reference the pbuf to spare it from freeing */ } else { /* no room in the queue */ /* the pbuf will be freed by the lwIP code */ printf("No room in driver pBuf queue\r\n"); return ERR_MEM; } } return ERR_OK; } /*==========================================================================*/ static err_t ethernetif_init(struct netif *netif) { tapdev_init(netif); netif->mtu = 1500; /* maximum transfer unit */ /* set device capabilities */ netif->flags = (NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP); return ERR_OK; } /*..........................................................................*/ /* * This function should do the actual transmission of the packet. The packet is * contained in the pbuf that is passed to the function. This pbuf might be * chained. * * @param p the MAC packet to send (e.g. IP packet including MAC addr and type) * @return ERR_OK if the packet could be sent * an err_t value if the packet couldn't be sent * @note This function MUST be called with interrupts disabled or with the * Stellaris Ethernet transmit fifo protected. */ static void low_level_transmit(struct pbuf *p) { struct pbuf *q; #if ETH_PAD_SIZE pbuf_header( p, -ETH_PAD_SIZE ); /* drop the padding word */ #endif EMAC_RequestSend(p->tot_len); for( q = p; q != NULL; q = q->next ) { /* Send the data from the pbuf to the interface, one pbuf at a time. The size of the data in each pbuf is kept in the ->len variable. if q->next == NULL then this is the last pbuf in the chain. */ //tapdev_send_frame( q->payload, q->len, ( q->next == NULL )); EMAC_CopyToFrame_Start(q->payload, q->len); } //signal that packet should be sent(); EMAC_CopyToFrame_End(); #if ETH_PAD_SIZE pbuf_header( p, ETH_PAD_SIZE ); /* reclaim the padding word */ #endif #if LINK_STATS LINK_STATS_INC(link.xmit); #endif /* LINK_STATS */ } /*..........................................................................*/ /* * This function will read a single packet from the Stellaris ethernet * interface, if available, and return a pointer to a pbuf. The timestamp * of the packet will be placed into the pbuf structure. * * @return pointer to pbuf packet if available, NULL otherswise. */ static struct pbuf *low_level_receive(void) { struct pbuf *p = NULL, *q; u16_t len; #if LWIP_PTPD u32_t time_s, time_ns; /* Get the current timestamp if PTPD is enabled */ lwIPHostGetTime(&time_s, &time_ns); #endif // Get length of ethernet packet. len = EMAC_StartReadFrame(); if(len) { #if ETH_PAD_SIZE len += ETH_PAD_SIZE; /* allow room for Ethernet padding */ #endif /* We allocate a pbuf chain of pbufs from the pool. */ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); /* If a pbuf was allocated, read the packet into the pbuf. */ if (p != NULL) { #if ETH_PAD_SIZE pbuf_header( p, -ETH_PAD_SIZE ); /* drop the padding word */ #endif /* Process all but the last buffer in the pbuf chain. */ for( q = p; q != NULL; q = q->next ) { /* Read enough bytes to fill this pbuf in the chain. The available data in the pbuf is given by the q->len variable. */ //tapdev_read_partial(q->payload, q->len, len); EMAC_CopyFromFrame(q->payload, q->len); } EMAC_EndReadFrame(); #if ETH_PAD_SIZE pbuf_header( p, ETH_PAD_SIZE ); /* reclaim the padding word */ #endif #if LINK_STATS /* Adjust the link statistics */ LINK_STATS_INC(link.recv); #endif #if LWIP_PTPD /* Place the timestamp in the PBUF */ p->time_s = time_s; p->time_ns = time_ns; #endif } else { #if LINK_STATS /* Adjust the link statistics */ EMAC_EndReadFrame(); // Drop packet LINK_STATS_INC(link.memerr); LINK_STATS_INC(link.drop); #endif } } return p; } /*..........................................................................*/ static void PbufQueue_ctor(PbufQueue *me) { me->qread = 0; me->qwrite = 0; me->overflow = 0; } /*..........................................................................*/ static struct pbuf *PbufQueue_get(PbufQueue *me) { struct pbuf *pBuf; if (PbufQueue_isEmpty(me)) { /* Return a NULL pointer if the queue is empty. */ pBuf = (struct pbuf *)0; } else { /* * The queue is not empty so return the next frame from it * and adjust the read pointer accordingly. */ pBuf = me->ring[me->qread]; if ((++me->qread) == Q_DIM(me->ring)) { me->qread = 0; } } return pBuf; } /*..........................................................................*/ static uint8_t PbufQueue_put(PbufQueue *me, struct pbuf *p) { uint8_t next_qwrite = me->qwrite + 1; if (next_qwrite == Q_DIM(me->ring)) { next_qwrite = 0; } if (next_qwrite != me->qread) { /* * The queue isn't full so we add the new frame at the current * write position and move the write pointer. */ me->ring[me->qwrite] = p; if ((++me->qwrite) == Q_DIM(me->ring)) { me->qwrite = 0; } return 1; /* successfully posted the pbuf */ } else { /* * The stack is full so we are throwing away this value. * Keep track of the number of times this happens. */ ++me->overflow; return 0; /* could not post the pbuf */ } } /*..........................................................................*/ #if NETIF_DEBUG /* Print an IP header by using LWIP_DEBUGF * @param p an IP packet, p->payload pointing to the IP header */ void eth_driver_debug_print(struct pbuf *p) { struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload; u16_t *plen = (u16_t *)p->payload; LWIP_DEBUGF(NETIF_DEBUG, ("ETH header:\n")); LWIP_DEBUGF(NETIF_DEBUG, ("Packet Length:%5"U16_F" \n",*plen)); LWIP_DEBUGF(NETIF_DEBUG, ("Destination: %02"X8_F"-%02"X8_F"-%02"X8_F "-%02"X8_F"-%02"X8_F"-%02"X8_F"\n", ethhdr->dest.addr[0], ethhdr->dest.addr[1], ethhdr->dest.addr[2], ethhdr->dest.addr[3], ethhdr->dest.addr[4], ethhdr->dest.addr[5])); LWIP_DEBUGF(NETIF_DEBUG, ("Source: %02"X8_F"-%02"X8_F"-%02"X8_F "-%02"X8_F"-%02"X8_F"-%02"X8_F"\n", ethhdr->src.addr[0], ethhdr->src.addr[1], ethhdr->src.addr[2], ethhdr->src.addr[3], ethhdr->src.addr[4], ethhdr->src.addr[5])); LWIP_DEBUGF(NETIF_DEBUG, ("Packet Type:0x%04"U16_F" \n", ethhdr->type)); } #endif /* NETIF_DEBUG */