/*
* 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 */