Hi Kieran,

Replies in-line below.


> Just to be sure: which end is running lwIP?  I think it's 192.168.0.100
> (the http server)
>

That is correct.

>
> A few things:
>  - everything looks mostly fine for the first 10 seconds or so of the
> trace.
>  - then I can see the server making increasing numbers of unnecessary
> retransmissions.  i.e. retransmitting packets that the client has
> acknowledged as received.  This is probably the root of the problem.
>  - then we start to see connections being reset by the server.  This in
> itself is a bit odd, unless your server normally works by aborting
> rather than closing connections.
>  - the client doesn't seem to be expecting the resets, and even seems to
> ignore them: it carries on trying to use those connections
> retransmitting packets and so the server repeatedly replies to them with
> resets.
>
> So, yes something is wrong.  I'm not sure what though.
>

I think most of my issues are to do with running out of memory buffers.
I managed to get printf debugging working out the serial port and with the
 MEM_DEBUG and MEMP_DEBUG debug zones turned on, was able to see that
various buffers were running out.
I increased the size of these as much as I could (I only have 32K on this
micro) and I am getting a lot less errors in wireshark, and the page is
loading much faster ... but there is still an issue.

>
> Could you try experimenting with fewer concurrent connections and see if
> that is the trigger?  How does you web server deal with concurrent
> connections?


I am only opening one connection at present (web browser on 192.168.0.100)

>  Does it for example use multiple threads?  Which API are
> you using in your web server to send and receive packets?
>

Using the RAW API.

>
> It would be useful if you could show us some of your code, particularly
> around how your application interfaces to lwIP, and how your driver
> interfaces to lwIP.  These are the most common areas for problems.
>

The code that interfaces the application (both web server and UDP handling)
is below:

 ------------------------------------------------------------------------------
me->netif = eth_driver_init((QActive *)me, macaddr);

me->ip_addr = 0xFFFFFFFF;             // initialize to impossible value

    // initialize the lwIP applications...
httpd_init();         // initialize the simple HTTP-Deamon (web server)
http_set_ssi_handler(&ssi_handler, ssi_tags, Q_DIM(ssi_tags));
http_set_cgi_handlers(cgi_handlers, Q_DIM(cgi_handlers));

me->upcb = udp_new();
udp_bind(me->upcb, IP_ADDR_ANY, 1235);           // use port 1235 for UDP
udp_recv(me->upcb, &udp_rx_handler, me);
---------------------------------------------------------------------------------

The http_init() function to set up the TCP callbacks is:

void httpd_init(void) {
    struct tcp_pcb *pcb;

    DEBUG_PRINT("httpd_init\n");

    pcb = tcp_new();
    tcp_bind(pcb, IP_ADDR_ANY, 80);
    pcb = tcp_listen(pcb);
    tcp_accept(pcb, http_accept);
}

As far as the driver is concerned, there are a lot of files so not sure what
to include.
I have attached the top level file.

Many Thanks,
Bernie

-----------------------------------------------------------------------------


> Kieran
>
>
>
> _______________________________________________
> lwip-users mailing list
> [email protected]
> http://lists.nongnu.org/mailman/listinfo/lwip-users
>



-- 
You always have believers and scepticts.. A True inventor is always a
believer..
/*
 * 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 <[email protected]>
 */

/*
 * 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 <string.h>                                         /* 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     = &etharp_output;

    l_netif.linkoutput = &ethernetif_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 */
              &ethernetif_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 */
_______________________________________________
lwip-users mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/lwip-users

Reply via email to