summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-06-22 11:27:57 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-06-22 11:27:57 -0600
commit0c72d9900e00e06e9c9b1008e795766011a9c46f (patch)
tree0a7b6144118d955836693cd0d31b2d9127418de7
parent8cf46d1f34452aca50d6aabe3d13bd12f54d5906 (diff)
downloadnuttx-0c72d9900e00e06e9c9b1008e795766011a9c46f.tar.gz
nuttx-0c72d9900e00e06e9c9b1008e795766011a9c46f.tar.bz2
nuttx-0c72d9900e00e06e9c9b1008e795766011a9c46f.zip
First cut at conversion of write-buffering to use I/O buffer chaings (IOBs)
-rw-r--r--nuttx/include/nuttx/net/iob.h30
-rw-r--r--nuttx/include/nuttx/net/uip/uip-tcp.h41
-rw-r--r--nuttx/include/nuttx/net/uip/uip.h9
-rw-r--r--nuttx/include/nuttx/net/uip/uipopt.h24
-rw-r--r--nuttx/net/iob/Kconfig12
-rw-r--r--nuttx/net/iob/Make.defs4
-rw-r--r--nuttx/net/iob/iob.h7
-rw-r--r--nuttx/net/iob/iob_add_queue.c4
-rw-r--r--nuttx/net/iob/iob_alloc.c100
-rw-r--r--nuttx/net/iob/iob_alloc_qentry.c111
-rw-r--r--nuttx/net/iob/iob_dump.c177
-rw-r--r--nuttx/net/iob/iob_free.c8
-rw-r--r--nuttx/net/iob/iob_free_chain.c20
-rw-r--r--nuttx/net/iob/iob_free_qentry.c13
-rw-r--r--nuttx/net/iob/iob_free_queue.c4
-rw-r--r--nuttx/net/iob/iob_initialize.c17
-rw-r--r--nuttx/net/iob/iob_remove_queue.c3
-rw-r--r--nuttx/net/net_send_buffered.c390
-rw-r--r--nuttx/net/net_send_unbuffered.c1
-rw-r--r--nuttx/net/tcp/Kconfig34
-rw-r--r--nuttx/net/tcp/Make.defs3
-rw-r--r--nuttx/net/tcp/tcp.h22
-rw-r--r--nuttx/net/tcp/tcp_wrbuffer.c65
-rw-r--r--nuttx/net/tcp/tcp_wrbuffer_dump.c89
-rw-r--r--nuttx/net/uip/Make.defs6
-rw-r--r--nuttx/net/uip/uip_iobsend.c105
-rw-r--r--nuttx/net/uip/uip_send.c14
27 files changed, 1081 insertions, 232 deletions
diff --git a/nuttx/include/nuttx/net/iob.h b/nuttx/include/nuttx/net/iob.h
index ac3977538..34a59cc00 100644
--- a/nuttx/include/nuttx/net/iob.h
+++ b/nuttx/include/nuttx/net/iob.h
@@ -55,10 +55,12 @@
#define IOB_DATA(p) (&(p)->io_data[(p)->io_offset])
#define IOB_FREESPACE(p) (CONFIG_IOB_BUFSIZE - (p)->io_len - (p)->io_offset)
+#if CONFIG_IOB_NCHAINS > 0
/* Queue helpers */
-#define IOB_QINIT(q) do { (q)->qh_head = 0; (q)->qh_tail = 0; } while (0)
-#define IOB_QEMPTY(q) ((q)->head == NULL)
+# define IOB_QINIT(q) do { (q)->qh_head = 0; (q)->qh_tail = 0; } while (0)
+# define IOB_QEMPTY(q) ((q)->head == NULL)
+#endif
/****************************************************************************
* Public Types
@@ -89,6 +91,7 @@ struct iob_s
uint8_t io_data[CONFIG_IOB_BUFSIZE];
};
+#if CONFIG_IOB_NCHAINS > 0
/* This container structure supports queuing of I/O buffer chains. This
* structure is intended only for internal use by the IOB module.
*/
@@ -113,6 +116,7 @@ struct iob_queue_s
FAR struct iob_qentry_s *qh_head;
FAR struct iob_qentry_s *qh_tail;
};
+#endif /* CONFIG_IOB_NCHAINS > 0 */
/****************************************************************************
* Global Data
@@ -173,7 +177,9 @@ void iob_free_chain(FAR struct iob_s *iob);
*
****************************************************************************/
+#if CONFIG_IOB_NCHAINS > 0
int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq);
+#endif /* CONFIG_IOB_NCHAINS > 0 */
/****************************************************************************
* Name: iob_add_queue
@@ -183,7 +189,9 @@ int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq);
*
****************************************************************************/
+#if CONFIG_IOB_NCHAINS > 0
FAR struct iob_s *iob_remove_queue(FAR struct iob_queue_s *iobq);
+#endif /* CONFIG_IOB_NCHAINS > 0 */
/****************************************************************************
* Name: iob_free_queue
@@ -193,7 +201,9 @@ FAR struct iob_s *iob_remove_queue(FAR struct iob_queue_s *iobq);
*
****************************************************************************/
+#if CONFIG_IOB_NCHAINS > 0
void iob_free_queue(FAR struct iob_queue_s *qhead);
+#endif /* CONFIG_IOB_NCHAINS > 0 */
/****************************************************************************
* Name: iob_copyin
@@ -278,11 +288,25 @@ FAR struct iob_s *iob_pack(FAR struct iob_s *iob);
* Name: iob_contig
*
* Description:
- * Ensure that there is'len' bytes of contiguous space at the beginning
+ * Ensure that there is 'len' bytes of contiguous space at the beginning
* of the I/O buffer chain starting at 'iob'.
*
****************************************************************************/
int iob_contig(FAR struct iob_s *iob, unsigned int len);
+/****************************************************************************
+ * Function: iob_dump
+ *
+ * Description:
+ * Dump the contents of a I/O buffer chain
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEBUG
+void iob_dump(FAR const char *msg, FAR struct iob_s *iob);
+#else
+# define tcp_writebuffer_dump(wrb)
+#endif
+
#endif /* _INCLUDE_NUTTX_NET_IOB_H */
diff --git a/nuttx/include/nuttx/net/uip/uip-tcp.h b/nuttx/include/nuttx/net/uip/uip-tcp.h
index cbb125a74..a2234c60d 100644
--- a/nuttx/include/nuttx/net/uip/uip-tcp.h
+++ b/nuttx/include/nuttx/net/uip/uip-tcp.h
@@ -56,6 +56,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <nuttx/net/uip/uipopt.h>
+#include <nuttx/net/uip/uip.h>
/****************************************************************************
* Pre-processor Definitions
@@ -123,6 +124,25 @@
# define UIP_TCP_INITIAL_MSS UIP_TCP_MSS
#endif
+#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
+/* TCP write buffer access macros */
+
+# define WRB_SEQNO(wrb) ((wrb)->wb_seqno)
+# define WRB_PKTLEN(wrb) ((wrb)->wb_iob->io_pktlen)
+# define WRB_SENT(wrb) ((wrb)->wb_sent)
+# define WRB_NRTX(wrb) ((wrb)->wb_nrtx)
+# define WRB_IOB(wrb) ((wrb)->wb_iob)
+# define WRB_COPYOUT(wrb,dest,n) (iob_copyout(dest,(wrb)->wb_iob,(n),0))
+# define WRB_COPYIN(wrb,src,n) (iob_copyin((wrb)->wb_iob,src,(n),0))
+# define WRB_TRIM(wrb,n) (iob_trimhead((wrb)->wb_iob,(n)))
+
+#ifdef CONFIG_DEBUG
+# define WRB_DUMP(msg,wrb) tcp_writebuffer_dump(msg,wrb)
+#else
+# define WRB_DUMP(mgs,wrb)
+#endif
+#endif
+
/****************************************************************************
* Public Type Definitions
****************************************************************************/
@@ -170,22 +190,28 @@ struct uip_conn
/* Read-ahead buffering.
*
- * readahead - A singly linked list of type struct uip_readahead_s
- * where the TCP/IP read-ahead data is retained.
+ * readahead - A singly linked list of type struct uip_readahead_s
+ * where the TCP/IP read-ahead data is retained.
*/
#ifdef CONFIG_NET_TCP_READAHEAD
sq_queue_t readahead; /* Read-ahead buffering */
#endif
- /* Write buffering */
+ /* Write buffering
+ *
+ * write_q - The queue of unsent I/O buffers. The head of this
+ * list may be partially sent. FIFO ordering.
+ * unacked_q - A queue of completely sent, but unacked I/O buffer
+ * chains. Sequence number ordering.
+ */
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
sq_queue_t write_q; /* Write buffering for segments */
sq_queue_t unacked_q; /* Write buffering for un-ACKed segments */
- size_t expired; /* Number segments retransmitted but not yet ACKed,
+ uint16_t expired; /* Number segments retransmitted but not yet ACKed,
* it can only be updated at UIP_ESTABLISHED state */
- size_t sent; /* The number of bytes sent */
+ uint16_t sent; /* The number of bytes sent */
uint32_t isn; /* Initial sequence number */
#endif
@@ -261,14 +287,15 @@ struct uip_readahead_s
/* This structure supports TCP write buffering */
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
+struct iob_s; /* Forward reference */
struct tcp_wrbuffer_s
{
sq_entry_t wb_node; /* Supports a singly linked list */
uint32_t wb_seqno; /* Sequence number of the write segment */
- uint16_t wb_nbytes; /* Number of bytes available in this buffer */
+ uint16_t wb_sent; /* Number of bytes sent from the I/O buffer chain */
uint8_t wb_nrtx; /* The number of retransmissions for the last
* segment sent */
- uint8_t wb_buffer[CONFIG_NET_TCP_WRITE_BUFSIZE];
+ struct iob_s *wb_iob; /* Head of the I/O buffer chain */
};
#endif
diff --git a/nuttx/include/nuttx/net/uip/uip.h b/nuttx/include/nuttx/net/uip/uip.h
index 798b905ac..a0cf88f5c 100644
--- a/nuttx/include/nuttx/net/uip/uip.h
+++ b/nuttx/include/nuttx/net/uip/uip.h
@@ -391,7 +391,14 @@ extern int uip_lockedwait(sem_t *sem);
* len The maximum amount of data bytes to be sent.
*/
-extern void uip_send(struct uip_driver_s *dev, const void *buf, int len);
+extern void uip_send(FAR struct uip_driver_s *dev, FAR const void *buf,
+ int len);
+
+#ifdef CONFIG_NET_IOB
+struct iob_s;
+extern void uip_iobsend(FAR struct uip_driver_s *dev, FAR struct iob_s *buf,
+ unsigned int len, unsigned int offset);
+#endif
/* uIP convenience and converting functions.
*
diff --git a/nuttx/include/nuttx/net/uip/uipopt.h b/nuttx/include/nuttx/net/uip/uipopt.h
index 5059d7e1b..fb85b4ad7 100644
--- a/nuttx/include/nuttx/net/uip/uipopt.h
+++ b/nuttx/include/nuttx/net/uip/uipopt.h
@@ -305,30 +305,6 @@
# undef CONFIG_NET_NTCP_READAHEAD_BUFFERS
#endif
-#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
- /* Number of TCP write buffers */
-
-# ifndef CONFIG_NET_NTCP_WRITE_BUFFERS
-# define CONFIG_NET_NTCP_WRITE_BUFFERS 1
-# endif
-
- /* The size of one TCP write buffer */
-
-# ifndef CONFIG_NET_TCP_WRITE_BUFSIZE
-# define CONFIG_NET_TCP_WRITE_BUFSIZE UIP_TCP_MSS
-# endif
-
- /* The size of the write buffer should not exceed the maximum TCP MSS */
-
-# if CONFIG_NET_TCP_WRITE_BUFSIZE > UIP_TCP_MSS
-# error CONFIG_NET_TCP_WRITE_BUFSIZE must not exceed UIP_TCP_MSS
-# endif
-
-#else
-# undef CONFIG_NET_TCP_WRITE_BUFSIZE
-# undef CONFIG_NET_NTCP_WRITE_BUFFERS
-#endif
-
/* Delay after receive to catch a following packet. No delay should be
* required if TCP/IP read-ahead buffering is enabled.
*/
diff --git a/nuttx/net/iob/Kconfig b/nuttx/net/iob/Kconfig
index f7793afa8..3ae153dac 100644
--- a/nuttx/net/iob/Kconfig
+++ b/nuttx/net/iob/Kconfig
@@ -30,10 +30,16 @@ config IOB_BUFSIZE
config IOB_NCHAINS
int "Number of pre-allocated I/O buffer chain heads"
- default 8
+ default 0
---help---
- These tiny nodes are used as "containers" to suppor queueing of
+ These tiny nodes are used as "containers" to support queueing of
I/O buffer chains. This will limit the number of I/O transactions
- that can be "in-flight" at any give time.
+ that can be "in-flight" at any give time. The default value of
+ zero disables this features.
+
+ These generic I/O buffer chain containers are not currently used
+ by any logic in NuttX. That is because their other other specialized
+ I/O buffer chain containers that also carry a payload of usage
+ specific information.
endif # NET_IOB
diff --git a/nuttx/net/iob/Make.defs b/nuttx/net/iob/Make.defs
index e522d5885..205ef72ea 100644
--- a/nuttx/net/iob/Make.defs
+++ b/nuttx/net/iob/Make.defs
@@ -43,6 +43,10 @@ NET_CSRCS += iob_free_chain.c iob_free_qentry.c iob_free_queue.c
NET_CSRCS += iob_initialize.c iob_pack.c iob_remove_queue.c iob_trimhead.c
NET_CSRCS += iob_trimtail.c
+ifeq ($(CONFIG_DEBUG),y)
+NET_CSRCS += iob_dump.c
+endif
+
# Include iob build support
DEPPATH += --dep-path iob
diff --git a/nuttx/net/iob/iob.h b/nuttx/net/iob/iob.h
index 0507fa34d..03ea6d14d 100644
--- a/nuttx/net/iob/iob.h
+++ b/nuttx/net/iob/iob.h
@@ -42,6 +42,8 @@
#include <nuttx/config.h>
+#include <semaphore.h>
+
#include <nuttx/net/iob.h>
/****************************************************************************
@@ -64,6 +66,11 @@ extern FAR struct iob_s *g_iob_freelist;
extern FAR struct iob_qentry_s *g_iob_freeqlist;
+/* Counting semaphores that tracks the number of free IOBs/qentries */
+
+extern sem_t g_iob_sem;
+extern sem_t g_qentry_sem;
+
/****************************************************************************
* Public Data
****************************************************************************/
diff --git a/nuttx/net/iob/iob_add_queue.c b/nuttx/net/iob/iob_add_queue.c
index 0a2ac3d0a..8ecc556ea 100644
--- a/nuttx/net/iob/iob_add_queue.c
+++ b/nuttx/net/iob/iob_add_queue.c
@@ -47,6 +47,8 @@
#include "iob.h"
+#if CONFIG_IOB_NCHAINS > 0
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -102,3 +104,5 @@ int iob_add_queue(FAR struct iob_s *iob, FAR struct iob_queue_s *iobq)
return 0;
}
+
+#endif /* CONFIG_IOB_NCHAINS > 0 */
diff --git a/nuttx/net/iob/iob_alloc.c b/nuttx/net/iob/iob_alloc.c
index f9810bfa3..68e3fb602 100644
--- a/nuttx/net/iob/iob_alloc.c
+++ b/nuttx/net/iob/iob_alloc.c
@@ -39,6 +39,9 @@
#include <nuttx/config.h>
+#include <semaphore.h>
+#include <assert.h>
+
#include <nuttx/arch.h>
#include <nuttx/net/iob.h>
@@ -61,18 +64,19 @@
****************************************************************************/
/****************************************************************************
- * Public Functions
+ * Private Functions
****************************************************************************/
/****************************************************************************
- * Name: iob_alloc
+ * Name: iob_tryalloc
*
* Description:
- * Allocate an I/O buffer by taking the buffer at the head of the free list.
+ * Try to allocate an I/O buffer by taking the buffer at the head of the
+ * free list.
*
****************************************************************************/
-FAR struct iob_s *iob_alloc(void)
+static FAR struct iob_s *iob_tryalloc(void)
{
FAR struct iob_s *iob;
irqstate_t flags;
@@ -85,9 +89,12 @@ FAR struct iob_s *iob_alloc(void)
iob = g_iob_freelist;
if (iob)
{
- /* Remove the I/O buffer from the free list */
+ /* Remove the I/O buffer from the free list and decrement the counting
+ * semaphore that tracks the number of free IOBs.
+ */
g_iob_freelist = iob->io_flink;
+ DEBUGVERIFY(sem_trywait(&g_iob_sem));
irqrestore(flags);
/* Put the I/O buffer in a known state */
@@ -102,3 +109,86 @@ FAR struct iob_s *iob_alloc(void)
irqrestore(flags);
return NULL;
}
+
+/****************************************************************************
+ * Name: iob_allocwait
+ *
+ * Description:
+ * Allocate an I/O buffer, waiting if necessary. This function cannot be
+ * called from any interrupt level logic.
+ *
+ ****************************************************************************/
+
+static FAR struct iob_s *iob_allocwait(void)
+{
+ FAR struct iob_s *iob;
+ irqstate_t flags;
+ int ret;
+
+ /* The following must be atomic; interrupt must be disabled so that there
+ * is no conflict with interrupt level I/O buffer allocations. This is
+ * not as bad as it sounds because interrupts will be re-enabled while
+ * we are waiting for I/O buffers to become free.
+ */
+
+ flags = irqsave();
+ do
+ {
+ /* Try to get an I/O buffer. If successful, the semaphore count
+ * will be decremented atomically.
+ */
+
+ iob = iob_tryalloc();
+ if (!iob)
+ {
+ /* If not successful, then the semaphore count was less than or
+ * equal to zero (meaning that there are no free buffers). We
+ * need to wait for an I/O buffer to be released when the semaphore
+ * count will be incremented.
+ */
+
+ ret = sem_wait(&g_iob_sem);
+
+ /* When we wake up from wait, an I/O buffer was returned to
+ * the free list. However, if there are concurrent allocations
+ * from interrupt handling, then I suspect that there is a
+ * race condition. But no harm, we will just wait again in
+ * that case.
+ */
+ }
+ }
+ while (ret == OK && !iob);
+
+ irqrestore(flags);
+ return iob;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: iob_alloc
+ *
+ * Description:
+ * Allocate an I/O buffer by taking the buffer at the head of the free list.
+ *
+ ****************************************************************************/
+
+FAR struct iob_s *iob_alloc(void)
+{
+ /* Were we called from the interrupt level? */
+
+ if (up_interrupt_context())
+ {
+ /* Yes, then try to allocate an I/O buffer without waiting */
+
+ return iob_tryalloc();
+ }
+ else
+ {
+ /* Then allocate an I/O buffer, waiting as necessary */
+
+ return iob_allocwait();
+ }
+}
diff --git a/nuttx/net/iob/iob_alloc_qentry.c b/nuttx/net/iob/iob_alloc_qentry.c
index 0c29fbec1..21314c011 100644
--- a/nuttx/net/iob/iob_alloc_qentry.c
+++ b/nuttx/net/iob/iob_alloc_qentry.c
@@ -39,11 +39,16 @@
#include <nuttx/config.h>
+#include <semaphore.h>
+#include <assert.h>
+
#include <nuttx/arch.h>
#include <nuttx/net/iob.h>
#include "iob.h"
+#if CONFIG_IOB_NCHAINS > 0
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -61,6 +66,104 @@
****************************************************************************/
/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: iob_tryalloc_qentry
+ *
+ * Description:
+ * Try to allocate an I/O buffer chain container by taking the buffer at
+ * the head of the free list. This function is intended only for internal
+ * use by the IOB module.
+ *
+ ****************************************************************************/
+
+static FAR struct iob_qentry_s *iob_tryalloc_qentry(void)
+{
+ FAR struct iob_qentry_s *iobq;
+ irqstate_t flags;
+
+ /* We don't know what context we are called from so we use extreme measures
+ * to protect the free list: We disable interrupts very briefly.
+ */
+
+ flags = irqsave();
+ iobq = g_iob_freeqlist;
+ if (iobq)
+ {
+ /* Remove the I/O buffer chain container from the free list and
+ * decrement the counting semaphore that tracks the number of free
+ * containers.
+ */
+
+ g_iob_freeqlist = iobq->qe_flink;
+ DEBVERIFY(sem_trywait(&g_qentry_sem));
+
+ /* Put the I/O buffer in a known state */
+
+ iobq->qe_head = NULL; /* Nothing is contained */
+ }
+
+ irqrestore(flags);
+ return iobq;
+}
+
+/****************************************************************************
+ * Name: iob_allocwait_qentry
+ *
+ * Description:
+ * Allocate an I/O buffer chain container by taking the buffer at the head
+ * of the free list. This function is intended only for internal use by
+ * the IOB module.
+ *
+ ****************************************************************************/
+
+static FAR struct iob_qentry_s *iob_allocwait_qentry(void)
+{
+ FAR struct iob_qentry_s *qentry;
+ irqstate_t flags;
+ int ret;
+
+ /* The following must be atomic; interrupt must be disabled so that there
+ * is no conflict with interrupt level I/O buffer chain container
+ * allocations. This is not as bad as it sounds because interrupts will be
+ * re-enabled while we are waiting for I/O buffers to become free.
+ */
+
+ flags = irqsave();
+ do
+ {
+ /* Try to get an I/O buffer chain container. If successful, the
+ * semaphore count will be decremented atomically.
+ */
+
+ qentry = iob_tryalloc_qentry();
+ if (!qentry)
+ {
+ /* If not successful, then the semaphore count was less than or
+ * equal to zero (meaning that there are no free buffers). We
+ * need to wait for an I/O buffer chain container to be released
+ * when the semaphore count will be incremented.
+ */
+
+ ret = sem_wait(&g_qentry_sem);
+
+ /* When we wake up from wait, an I/O buffer chain container was
+ * returned to the free list. However, if there are concurrent
+ * allocations from interrupt handling, then I suspect that there
+ * is a race condition. But no harm, we will just wait again in
+ * that case.
+ */
+ }
+ }
+ while (ret == OK && !qentry);
+
+ irqrestore(flags);
+ return qentry;
+}
+
+/****************************************************************************
* Public Functions
****************************************************************************/
@@ -87,9 +190,13 @@ FAR struct iob_qentry_s *iob_alloc_qentry(void)
iobq = g_iob_freeqlist;
if (iobq)
{
- /* Remove the I/O buffer chain container from the free list */
+ /* Remove the I/O buffer chain container from the free list and
+ * decrement the counting semaphore that tracks the number of free
+ * containers.
+ */
g_iob_freeqlist = iobq->qe_flink;
+ DEBVERIFY(sem_trywait(&g_qentry_sem));
/* Put the I/O buffer in a known state */
@@ -99,3 +206,5 @@ FAR struct iob_qentry_s *iob_alloc_qentry(void)
irqrestore(flags);
return iobq;
}
+
+#endif /* CONFIG_IOB_NCHAINS > 0 */
diff --git a/nuttx/net/iob/iob_dump.c b/nuttx/net/iob/iob_dump.c
new file mode 100644
index 000000000..a4d1cf04a
--- /dev/null
+++ b/nuttx/net/iob/iob_dump.c
@@ -0,0 +1,177 @@
+/****************************************************************************
+ * net/iob/iob_dump.c
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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
+ * COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <debug.h>
+
+#include <nuttx/net/iob.h>
+
+#ifdef CONFIG_DEBUG
+
+/****************************************************************************
+ * Pre-processor definitions
+ ****************************************************************************/
+
+/* Select the lowest level debug interface available */
+
+#ifdef CONFIG_CPP_HAVE_VARARGS
+# ifdef CONFIG_ARCH_LOWPUTC
+# define message(format, ...) lowsyslog(format, ##__VA_ARGS__)
+# else
+# define message(format, ...) syslog(format, ##__VA_ARGS__)
+# endif
+#else
+# ifdef CONFIG_ARCH_LOWPUTC
+# define message lowsyslog
+# else
+# define message syslog
+# endif
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Function: iob_dump
+ *
+ * Description:
+ * Dump the contents of a I/O buffer chain
+ *
+ ****************************************************************************/
+
+void iob_dump(FAR const char *msg, FAR struct iob_s *iob)
+{
+ FAR struct iob_s *head = iob;
+ FAR const uint8_t *buffer;
+ uint8_t data[32];
+ unsigned int nbytes;
+ unsigned int i;
+ unsigned int j;
+ int len;
+
+ message("%s: IOB=%p pktlen=%d\n", msg, head, head->io_pktlen);
+
+ buffer = &iob->io_data[iob->io_offset];
+ len = iob->io_len;
+
+ for (i = 0; i < head->io_pktlen && iob; i += 32)
+ {
+ /* Copy 32-bytes into our local buffer */
+
+ for (nbytes = 0; nbytes < 32; nbytes++)
+ {
+ data[nbytes] = *buffer++;
+
+ /* If we have exhausted the data in this I/O buffer,
+ * then skip to the next I/O buffer in the chain.
+ */
+
+ if (--len <= 0)
+ {
+ iob = iob->io_flink;
+ if (!iob)
+ {
+ /* Ooops... we are at the end of the chain.
+ * break out with iob = NULL, len == 0, and
+ * nbytes <= 32.
+ */
+
+ len = 0;
+ break;
+ }
+
+ /* Get the data from the next I/O buffer in the chain */
+
+ buffer = &iob->io_data[iob->io_offset];
+ len = iob->io_len;
+ }
+ }
+
+ /* Make sure that we have something to print */
+
+ if (nbytes > 0)
+ {
+ message("%04x: ", i);
+ for (j = 0; j < 32; j++)
+ {
+ if (j == 16)
+ {
+ message(" ");
+ }
+
+ if (i + j < head->io_pktlen)
+ {
+ message("%02x", buffer[j]);
+ }
+ else
+ {
+ message(" ");
+ }
+ }
+
+ message(" ");
+ for (j = 0; j < 32; j++)
+ {
+ if (j == 16)
+ {
+ message(" ");
+ }
+
+ if (i + j < head->io_pktlen)
+ {
+ if (buffer[j] >= 0x20 && buffer[j] < 0x7f)
+ {
+ message("%c", buffer[j]);
+ }
+ else
+ {
+ message(".");
+ }
+ }
+ }
+
+ message("\n");
+ }
+ }
+}
+
+#endif /* CONFIG_DEBUG */
diff --git a/nuttx/net/iob/iob_free.c b/nuttx/net/iob/iob_free.c
index 75d70e080..e2c95aae0 100644
--- a/nuttx/net/iob/iob_free.c
+++ b/nuttx/net/iob/iob_free.c
@@ -39,6 +39,7 @@
#include <nuttx/config.h>
+#include <semaphore.h>
#include <assert.h>
#include <nuttx/arch.h>
@@ -86,7 +87,6 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob)
if (next)
{
-
/* Copy and decrement the total packet length, being careful to
* do nothing too crazy.
*/
@@ -115,8 +115,12 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob)
*/
flags = irqsave();
- iob->io_flink = g_iob_freelist;
+ iob->io_flink = g_iob_freelist;
g_iob_freelist = iob;
+
+ /* Signal that an IOB is available */
+
+ sem_post(&g_iob_sem);
irqrestore(flags);
/* And return the I/O buffer after the one that was freed */
diff --git a/nuttx/net/iob/iob_free_chain.c b/nuttx/net/iob/iob_free_chain.c
index db9fc408c..320cdfd07 100644
--- a/nuttx/net/iob/iob_free_chain.c
+++ b/nuttx/net/iob/iob_free_chain.c
@@ -75,20 +75,12 @@
void iob_free_chain(FAR struct iob_s *iob)
{
- FAR struct iob_s *last;
- irqstate_t flags;
+ FAR struct iob_s *next;
- /* Find the last entry in the I/O buffer list */
+ /* Free each IOB in the chain -- one at a time to keep the count straight */
- for (last = iob; last->io_flink; last = last->io_flink);
-
- /* Free the I/O buffer chain by adding it to the head of the free list. We
- * don't know what context we are called from so we use extreme measures to
- * protect the free list: We disable interrupts very briefly.
- */
-
- flags = irqsave();
- last->io_flink = g_iob_freelist;
- g_iob_freelist = iob;
- irqrestore(flags);
+ for (; iob; iob = next)
+ {
+ next = iob_free(iob);
+ }
}
diff --git a/nuttx/net/iob/iob_free_qentry.c b/nuttx/net/iob/iob_free_qentry.c
index 55d4cf848..db0f3d78a 100644
--- a/nuttx/net/iob/iob_free_qentry.c
+++ b/nuttx/net/iob/iob_free_qentry.c
@@ -39,6 +39,7 @@
#include <nuttx/config.h>
+#include <semaphore.h>
#include <assert.h>
#include <nuttx/arch.h>
@@ -46,6 +47,8 @@
#include "iob.h"
+#if CONFIG_IOB_NCHAINS > 0
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -70,7 +73,7 @@
* Name: iob_free_qentry
*
* Description:
- * Free the I/O buffer chain container by returning it to the free list.
+ * Free the I/O buffer chain container by returning it to the free list.
* The link to the next I/O buffer in the chain is return.
*
****************************************************************************/
@@ -86,11 +89,17 @@ FAR struct iob_qentry_s *iob_free_qentry(FAR struct iob_qentry_s *iobq)
*/
flags = irqsave();
- iobq->qe_flink = g_iob_freeqlist;
+ iobq->qe_flink = g_iob_freeqlist;
g_iob_freeqlist = iobq;
+
+ /* Signal that an I/O buffer chain container is available */
+
+ sem_post(&g_qentry_sem);
irqrestore(flags);
/* And return the I/O buffer chain container after the one that was freed */
return nextq;
}
+
+#endif /* CONFIG_IOB_NCHAINS > 0 */
diff --git a/nuttx/net/iob/iob_free_queue.c b/nuttx/net/iob/iob_free_queue.c
index 1b1a51888..d9af47d6c 100644
--- a/nuttx/net/iob/iob_free_queue.c
+++ b/nuttx/net/iob/iob_free_queue.c
@@ -45,6 +45,8 @@
#include "iob.h"
+#if CONFIG_IOB_NCHAINS > 0
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -112,3 +114,5 @@ void iob_free_queue(FAR struct iob_queue_s *qhead)
iob_free_chain(iob);
}
}
+
+#endif /* CONFIG_IOB_NCHAINS > 0 */
diff --git a/nuttx/net/iob/iob_initialize.c b/nuttx/net/iob/iob_initialize.c
index 5c4c122c3..2a9b527c0 100644
--- a/nuttx/net/iob/iob_initialize.c
+++ b/nuttx/net/iob/iob_initialize.c
@@ -40,6 +40,7 @@
#include <nuttx/config.h>
#include <stdbool.h>
+#include <semaphore.h>
#include <nuttx/net/iob.h>
@@ -60,7 +61,9 @@
/* This is a pool of pre-allocated I/O buffers */
static struct iob_s g_iob_pool[CONFIG_IOB_NBUFFERS];
+#if CONFIG_IOB_NCHAINS > 0
static struct iob_qentry_s g_iob_qpool[CONFIG_IOB_NCHAINS];
+#endif
/****************************************************************************
* Public Data
@@ -72,7 +75,16 @@ FAR struct iob_s *g_iob_freelist;
/* A list of all free, unallocated I/O buffer queue containers */
+#if CONFIG_IOB_NCHAINS > 0
FAR struct iob_qentry_s *g_iob_freeqlist;
+#endif
+
+/* Counting semaphores that tracks the number of free IOBs/qentries */
+
+sem_t g_iob_sem;
+#if CONFIG_IOB_NCHAINS > 0
+sem_t g_qentry_sem;
+#endif
/****************************************************************************
* Public Functions
@@ -107,6 +119,9 @@ void iob_initialize(void)
g_iob_freelist = iob;
}
+ sem_init(&g_iob_sem, 0, CONFIG_IOB_NBUFFERS);
+
+#if CONFIG_IOB_NCHAINS > 0
/* Add each I/O buffer chain queue container to the free list */
for (i = 0; i < CONFIG_IOB_NCHAINS; i++)
@@ -119,6 +134,8 @@ void iob_initialize(void)
g_iob_freeqlist = iobq;
}
+ sem_init(&g_qentry_sem, 0, CONFIG_IOB_NCHAINS);
+#endif
initialized = true;
}
}
diff --git a/nuttx/net/iob/iob_remove_queue.c b/nuttx/net/iob/iob_remove_queue.c
index 7a64d47c7..88752a2e8 100644
--- a/nuttx/net/iob/iob_remove_queue.c
+++ b/nuttx/net/iob/iob_remove_queue.c
@@ -45,6 +45,8 @@
#include "iob.h"
+#if CONFIG_IOB_NCHAINS > 0
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -92,3 +94,4 @@ FAR struct iob_s *iob_remove_queue(FAR struct iob_queue_s *iobq)
return iob;
}
+#endif /* CONFIG_IOB_NCHAINS > 0 */
diff --git a/nuttx/net/net_send_buffered.c b/nuttx/net/net_send_buffered.c
index 3717b609b..87f3d61d0 100644
--- a/nuttx/net/net_send_buffered.c
+++ b/nuttx/net/net_send_buffered.c
@@ -64,6 +64,7 @@
#include <arch/irq.h>
#include <nuttx/clock.h>
#include <nuttx/net/arp.h>
+#include <nuttx/net/iob.h>
#include <nuttx/net/uip/uip-arch.h>
#include "net_internal.h"
@@ -92,8 +93,8 @@
* ascending order of sequence number.
*
* Parameters:
- * segment The segment to be inserted
- * q The write buffer queue in which to insert the segment
+ * wrb The segment to be inserted
+ * q The write buffer queue in which to insert the segment
*
* Returned Value:
* None
@@ -103,17 +104,17 @@
*
****************************************************************************/
-static void send_insert_seqment(FAR struct tcp_wrbuffer_s *segment,
+static void send_insert_seqment(FAR struct tcp_wrbuffer_s *wrb,
FAR sq_queue_t *q)
{
- sq_entry_t *entry = (sq_entry_t*)segment;
+ sq_entry_t *entry = (sq_entry_t*)wrb;
sq_entry_t *insert = NULL;
sq_entry_t *itr;
for (itr = sq_peek(q); itr; itr = sq_next(itr))
{
- FAR struct tcp_wrbuffer_s *segment0 = (FAR struct tcp_wrbuffer_s*)itr;
- if (segment0->wb_seqno < segment->wb_seqno)
+ FAR struct tcp_wrbuffer_s *wrb0 = (FAR struct tcp_wrbuffer_s*)itr;
+ if (WRB_SEQNO(wrb0) < WRB_SEQNO(wrb))
{
insert = itr;
}
@@ -134,6 +135,54 @@ static void send_insert_seqment(FAR struct tcp_wrbuffer_s *segment,
}
/****************************************************************************
+ * Function: lost_connection
+ *
+ * Description:
+ * The TCP connection has been lost. Free all write buffers.
+ *
+ * Parameters:
+ * psock The socket structure
+ * conn The connection structure associated with the socket
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static inline void lost_connection(FAR struct socket *psock,
+ FAR struct uip_conn *conn)
+{
+ FAR sq_entry_t *entry;
+ FAR sq_entry_t *next;
+
+ /* Do not allow any further callbacks */
+
+ psock->s_sndcb->flags = 0;
+ psock->s_sndcb->event = NULL;
+
+ /* Free all queued write buffers */
+
+ for (entry = sq_peek(&conn->unacked_q); entry; entry = next)
+ {
+ next = sq_next(entry);
+ tcp_wrbuffer_release((FAR struct tcp_wrbuffer_s *)entry);
+ }
+
+ for (entry = sq_peek(&conn->write_q); entry; entry = next)
+ {
+ next = sq_next(entry);
+ tcp_wrbuffer_release((FAR struct tcp_wrbuffer_s *)entry);
+ }
+
+ /* Reset write buffering variables */
+
+ sq_init(&conn->unacked_q);
+ sq_init(&conn->write_q);
+ conn->expired = 0;
+ conn->sent = 0;
+}
+
+/****************************************************************************
* Function: send_interrupt
*
* Description:
@@ -156,10 +205,10 @@ static void send_insert_seqment(FAR struct tcp_wrbuffer_s *segment,
static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
FAR void *pvpriv, uint16_t flags)
{
- FAR struct uip_conn *conn = (FAR struct uip_conn*)pvconn;
+ FAR struct uip_conn *conn = (FAR struct uip_conn *)pvconn;
FAR struct socket *psock = (FAR struct socket *)pvpriv;
- nllvdbg("flags: %04x\n", flags);
+ //nllvdbg("flags: %04x\n", flags);
/* If this packet contains an acknowledgement, then update the count of
* acknowledged bytes.
@@ -167,30 +216,101 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
if ((flags & UIP_ACKDATA) != 0)
{
+ FAR struct tcp_wrbuffer_s *wrb;
FAR sq_entry_t *entry;
FAR sq_entry_t *next;
- FAR struct tcp_wrbuffer_s *segment;
uint32_t ackno;
ackno = uip_tcpgetsequence(TCPBUF->ackno);
+ nllvdbg("ACK: ackno=%d flags=%04x\n", ackno, flags);
+
+ /* Look at every write buffer int he unacked_q. The unacked_q
+ * holds write buffers that have been entirely sent, but which
+ * have not yet been acked.
+ */
+
for (entry = sq_peek(&conn->unacked_q); entry; entry = next)
{
- next = sq_next(entry);
- segment = (FAR struct tcp_wrbuffer_s*)entry;
+ uint32_t lastseq;
+
+ /* Check of some or all of this write buffer has been ACKed. */
+
+ next = sq_next(entry);
+ wrb = (FAR struct tcp_wrbuffer_s*)entry;
+
+ /* If the ACKed sequence number is greater than the start
+ * sequence number of the write buffer, then some or all of
+ * the write buffer has been ACKed.
+ */
- if (segment->wb_seqno < ackno)
+ if (ackno > WRB_SEQNO(wrb))
{
- nllvdbg("ACK: acked=%d buflen=%d ackno=%d\n",
- segment->wb_seqno, segment->wb_nbytes, ackno);
+ /* Get the sequence number at the end of the data */
+
+ lastseq = WRB_SEQNO(wrb) + WRB_PKTLEN(wrb);
+ nllvdbg("ACK: seqno=%d lastseq=%d pktlen=%d ackno=%d\n",
+ WRB_SEQNO(wrb), lastseq, WRB_PKTLEN(wrb), ackno);
+
+ /* Has the entire buffer been ACKed? */
+
+ if (ackno >= lastseq)
+ {
+ /* Yes... Remove the write buffer from ACK waiting queue */
+
+ sq_rem(entry, &conn->unacked_q);
+
+ /* And return the write buffer to the pool of free buffers */
+
+ tcp_wrbuffer_release(wrb);
+ }
+ else
+ {
+ /* No, then just trim the ACKed bytes from the beginning
+ * of the write buffer. This will free up some I/O buffers
+ * that can be reused while are still sending the last
+ * buffers in the chain.
+ */
- /* Segment was ACKed. Remove from ACK waiting queue */
+ WRB_TRIM(wrb, ackno - WRB_SEQNO(wrb));
+ WRB_SEQNO(wrb) = ackno;
- sq_rem(entry, &conn->unacked_q);
+ nllvdbg("ACK: seqno=%d pktlen=%d\n",
+ WRB_SEQNO(wrb), WRB_PKTLEN(wrb));
+ }
+ }
+ }
+
+ /* A special case is the head of the write_q which may be partially
+ * sent and so can still have un-ACKed bytes that could get ACKed
+ * before the entire write buffer has even been sent.
+ */
+
+ wrb = (FAR struct tcp_wrbuffer_s*)sq_peek(&conn->write_q);
+ if (wrb && WRB_SENT(wrb) > 0 && ackno > WRB_SEQNO(wrb))
+ {
+ uint32_t nacked;
- /* Return the write buffer to the pool of free buffers */
+ /* Get the sequence number at the end of the data */
- tcp_wrbuffer_release(segment);
+ nacked = ackno - WRB_SEQNO(wrb);
+ if (nacked > WRB_SENT(wrb))
+ {
+ /* More data has been ACKed then we have sent? */
+
+ nacked = WRB_SENT(wrb);
}
+
+ nllvdbg("ACK: seqno=%d nacked=%d sent=%d ackno=%d\n",
+ WRB_SEQNO(wrb), nacked, WRB_SENT(wrb), ackno);
+
+ /* Trim the ACKed bytes from the beginning of the write buffer. */
+
+ WRB_TRIM(wrb, nacked);
+ WRB_SEQNO(wrb) = ackno;
+ WRB_SENT(wrb) -= nacked;
+
+ nllvdbg("ACK: seqno=%d pktlen=%d sent=%d\n",
+ WRB_SEQNO(wrb), WRB_PKTLEN(wrb), WRB_SENT(wrb));
}
}
@@ -198,35 +318,53 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
else if ((flags & (UIP_CLOSE | UIP_ABORT | UIP_TIMEDOUT)) != 0)
{
+ nllvdbg("Lost connection: %04x\n", flags);
+
/* Report not connected */
- nllvdbg("Lost connection\n");
- net_lostconnection(psock, flags);
- goto end_wait;
- }
+ net_lostconnection(psock, flags);
+
+ /* Free write buffers and terminate polling */
+
+ lost_connection(psock, conn);
+ return flags;
+ }
/* Check if we are being asked to retransmit data */
else if ((flags & UIP_REXMIT) != 0)
{
- sq_entry_t *entry;
+ FAR struct tcp_wrbuffer_s *wrb;
+ FAR sq_entry_t *entry;
- /* Put all segments that have been sent but not ACKed to write queue
- * again note, the un-ACKed segment is put at the first of the write_q,
- * so it can be sent as soon as possible.
+ nllvdbg("REXMIT: %04x\n", flags);
+
+ /* If there is a partially sent write buffer at the head of the
+ * write_q, reset the number of bytes sent.
*/
- while ((entry = sq_remlast(&conn->unacked_q)))
+ wrb = (FAR struct tcp_wrbuffer_s*)sq_peek(&conn->write_q);
+ if (wrb)
{
- struct tcp_wrbuffer_s *segment = (struct tcp_wrbuffer_s*)entry;
+ WRB_SENT(wrb) = 0;
+ }
+
+ /* Move all segments that have been sent but not ACKed to the write
+ * queue again note, the un-ACKed segments are put at the head of the
+ * write_q so they can be resent as soon as possible.
+ */
- if (segment->wb_nrtx >= UIP_MAXRTX)
- {
- //conn->unacked -= segment->wb_nbytes;
+ while ((entry = sq_remlast(&conn->unacked_q)) != NULL)
+ {
+ wrb = (FAR struct tcp_wrbuffer_s*)entry;
+
+ /* Free any write buffers that have exceed the retry count */
- /* Return the write buffer */
+ if (WRB_NRTX(wrb) >= UIP_MAXRTX)
+ {
+ /* Return the write buffer to the free list */
- tcp_wrbuffer_release(segment);
+ tcp_wrbuffer_release(wrb);
/* NOTE expired is different from un-ACKed, it is designed to
* represent the number of segments that have been sent,
@@ -239,8 +377,16 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
conn->expired++;
continue;
}
+ else
+ {
+ /* Insert the write buffer into the write_q (in sequence
+ * number order). The retransmission will occur below
+ * when the write buffer with the lowest sequenc number
+ * is pulled from the write_q again.
+ */
- send_insert_seqment(segment, &conn->write_q);
+ send_insert_seqment(wrb, &conn->write_q);
+ }
}
}
@@ -259,7 +405,7 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
* asked to retransmit data, (3) the connection is still healthy, and (4)
* the outgoing packet is available for our use. In this case, we are
* now free to send more data to receiver -- UNLESS the buffer contains
- * unprocesed incoming data. In that event, we will have to wait for the
+ * unprocessed incoming data. In that event, we will have to wait for the
* next polling cycle.
*/
@@ -283,44 +429,64 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
if (arp_find(conn->ripaddr) != NULL)
#endif
{
- FAR struct tcp_wrbuffer_s *segment;
- FAR void *sndbuf;
+ FAR struct tcp_wrbuffer_s *wrb;
size_t sndlen;
- /* Get the amount of data that we can send in the next packet */
+ /* Peek at the head of the write queue (but don't remove anything
+ * from the write queue yet.
+ */
- segment = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
- if (segment)
+ wrb = (FAR struct tcp_wrbuffer_s *)sq_peek(&conn->write_q);
+ if (wrb)
{
- sndbuf = segment->wb_buffer;
- sndlen = segment->wb_nbytes;
+ /* Get the amount of data that we can send in the next packet.
+ * We will send either the remaining data in the buffer I/O
+ * buffer chain, or as much as will fit given the MSS and current
+ * window size.
+ */
- DEBUGASSERT(sndlen <= uip_mss(conn));
+ sndlen = WRB_PKTLEN(wrb) - WRB_SENT(wrb);
+ if (sndlen > uip_mss(conn))
+ {
+ sndlen = uip_mss(conn);
+ }
- /* REVISIT: There should be a check here to assure that we do
- * not excced the window (conn->winsize).
- */
+ if (sndlen > conn->winsize)
+ {
+ sndlen = conn->winsize;
+ }
- /* Set the sequence number for this segment. NOTE: uIP
- * updates sndseq on receipt of ACK *before* this function
- * is called. In that case sndseq will point to the next
- * unacknowledged byte (which might have already been
- * sent). We will overwrite the value of sndseq here
- * before the packet is sent.
+ nllvdbg("pktlen=%d sent=%d sndlen=%d\n",
+ WRB_PKTLEN(wrb), WRB_SENT(wrb), sndlen);
+
+ /* Is this the first we have tried to send from this
+ * write buffer?
*/
- if (segment->wb_nrtx == 0 && segment->wb_seqno == (unsigned)-1)
+ if (WRB_SENT(wrb) == 0)
{
- segment->wb_seqno = conn->isn + conn->sent;
- }
+ /* Yes..Set the sequence number for this segment.
+ * NOTE: the TCP stack updates sndseq on receipt of ACK
+ * *before* this function is called. In that case sndseq
+ * will point to the next unacknowledged byte (which might
+ * have already been sent). We will overwrite the value of
+ * sndseq here before the packet is sent.
+ */
- uip_tcpsetsequence(conn->sndseq, segment->wb_seqno);
+ if (WRB_NRTX(wrb) == 0 && WRB_SEQNO(wrb) == (unsigned)-1)
+ {
+ WRB_SEQNO(wrb) = conn->isn + conn->sent;
+ }
+
+ uip_tcpsetsequence(conn->sndseq, WRB_SEQNO(wrb));
+ }
- /* Then set-up to send that amount of data. (this won't
- * actually happen until the polling cycle completes).
+ /* Then set-up to send that amount of data with the offset
+ * corresponding to the amount of data already sent. (this
+ * won't* actually happen until the polling cycle completes).
*/
- uip_send(dev, sndbuf, sndlen);
+ uip_iobsend(dev, WRB_IOB(wrb), sndlen, WRB_SENT(wrb));
/* Remember how much data we send out now so that we know
* when everything has been acknowledged. Just increment
@@ -330,7 +496,7 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
* this path.
*/
- if (segment->wb_nrtx == 0)
+ if (WRB_NRTX(wrb) == 0)
{
conn->unacked += sndlen;
conn->sent += sndlen;
@@ -343,11 +509,31 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
* second interval before expiration.
*/
- segment->wb_nrtx++;
+ WRB_NRTX(wrb)++;
+ nllvdbg("nrtx=%d unacked=%d sent=%d\n",
+ WRB_NRTX(wrb), conn->unacked, conn->sent);
- /* The segment is waiting for ACK again */
+ /* Remove the write buffer from the write queue if the
+ * last of the data has been sent from the buffer.
+ */
+
+ WRB_SENT(wrb) += sndlen;
+ DEBUGASSERT(WRB_SENT(wrb) <= WRB_PKTLEN(wrb));
+
+ if (WRB_SENT(wrb) >= WRB_PKTLEN(wrb))
+ {
+ FAR struct tcp_wrbuffer_s *tmp;
- send_insert_seqment(segment, &conn->unacked_q);
+ tmp = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
+ DEBUGASSERT(tmp == wrb);
+ UNUSED(tmp);
+
+ /* Put the I/O buffer chin in the unacked queue; the
+ * segment is waiting for ACK again
+ */
+
+ send_insert_seqment(wrb, &conn->unacked_q);
+ }
/* Only one data can be sent by low level driver at once,
* tell the caller stop polling the other connection.
@@ -361,15 +547,6 @@ static uint16_t send_interrupt(FAR struct uip_driver_s *dev, FAR void *pvconn,
/* Continue waiting */
return flags;
-
-end_wait:
-
- /* Do not allow any further callbacks */
-
- psock->s_sndcb->flags = 0;
- psock->s_sndcb->event = NULL;
-
- return flags;
}
/****************************************************************************
@@ -444,7 +621,7 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len,
int flags)
{
uip_lock_t save;
- ssize_t completed = 0;
+ ssize_t result = 0;
int err;
int ret = OK;
@@ -477,16 +654,19 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len,
psock->s_sndcb = uip_tcpcallbackalloc(conn);
}
- /* Test if the callback has been allocated*/
+ /* Test if the callback has been allocated */
if (!psock->s_sndcb)
{
/* A buffer allocation error occurred */
- completed = -ENOMEM;
+ ndbg("ERROR: Failed to allocate callback\n");
+ result = -ENOMEM;
}
else
{
+ FAR struct tcp_wrbuffer_s *wrb = tcp_wrbuffer_alloc();
+
/* Set up the callback in the connection */
psock->s_sndcb->flags = (UIP_ACKDATA | UIP_REXMIT |UIP_POLL | \
@@ -494,47 +674,35 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len,
psock->s_sndcb->priv = (void*)psock;
psock->s_sndcb->event = send_interrupt;
- while (completed < len)
- {
- FAR struct tcp_wrbuffer_s *segment = tcp_wrbuffer_alloc(NULL);
- if (segment)
- {
- size_t cnt;
-
- segment->wb_seqno = (unsigned)-1;
- segment->wb_nrtx = 0;
+ /* Allocate an write buffer */
- if (len - completed > CONFIG_NET_TCP_WRITE_BUFSIZE)
- {
- cnt = CONFIG_NET_TCP_WRITE_BUFSIZE;
- }
- else
- {
- cnt = len - completed;
- }
+ wrb = tcp_wrbuffer_alloc();
+ if (wrb)
+ {
+ /* Initialize the write buffer */
- segment->wb_nbytes = cnt;
- memcpy(segment->wb_buffer, (char*)buf + completed, cnt);
- completed += cnt;
+ WRB_SEQNO(wrb) = (unsigned)-1;
+ WRB_NRTX(wrb) = 0;
+ WRB_COPYIN(wrb, (FAR uint8_t *)buf, len);
- /* send_interrupt() will refer to all the write buffer by
- * conn->writebuff
- */
+ /* send_interrupt() will send data in FIFO order from the
+ * conn->write_q
+ */
- sq_addlast(&segment->wb_node, &conn->write_q);
+ sq_addlast(&wrb->wb_node, &conn->write_q);
- /* Notify the device driver of the availability of TX data */
+ /* Notify the device driver of the availability of TX data */
- netdev_txnotify(conn->ripaddr);
- }
+ netdev_txnotify(conn->ripaddr);
+ result = len;
+ }
- /* A buffer allocation error occurred */
+ /* A buffer allocation error occurred */
- else
- {
- completed = -ENOMEM;
- break;
- }
+ else
+ {
+ ndbg("ERROR: Failed to allocate write buffer\n");
+ result = -ENOMEM;
}
}
}
@@ -549,9 +717,9 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len,
* for the send length
*/
- if (completed < 0)
+ if (result < 0)
{
- err = completed;
+ err = result;
goto errout;
}
@@ -567,7 +735,7 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len,
/* Return the number of bytes actually sent */
- return completed;
+ return result;
errout:
set_errno(err);
diff --git a/nuttx/net/net_send_unbuffered.c b/nuttx/net/net_send_unbuffered.c
index 10c623ddb..d9d3dc9c9 100644
--- a/nuttx/net/net_send_unbuffered.c
+++ b/nuttx/net/net_send_unbuffered.c
@@ -467,7 +467,6 @@ static uint16_t tcpsend_interrupt(FAR struct uip_driver_s *dev,
uint32_t sndlen = pstate->snd_buflen - pstate->snd_sent;
-
#if defined(CONFIG_NET_TCP_SPLIT)
/* RFC 1122 states that a host may delay ACKing for up to 500ms but
diff --git a/nuttx/net/tcp/Kconfig b/nuttx/net/tcp/Kconfig
index 1d41e762a..64e13781d 100644
--- a/nuttx/net/tcp/Kconfig
+++ b/nuttx/net/tcp/Kconfig
@@ -82,6 +82,7 @@ endif # NET_TCP_READAHEAD
config NET_TCP_WRITE_BUFFERS
bool "Enable TCP/IP write buffering"
default n
+ select NET_IOB
---help---
Write buffers allows buffering of ongoing TCP/IP packets, providing
for higher performance, streamed output.
@@ -91,34 +92,15 @@ config NET_TCP_WRITE_BUFFERS
if NET_TCP_WRITE_BUFFERS
-config NET_TCP_WRITE_BUFSIZE
- int "TCP/IP write buffer size"
- default 1220 if !NET_SLIP && NET_IPv6
- default 536 if !NET_SLIP && !NET_IPv6
- default 256 if NET_SLIP && !NET_IPv6
- ---help---
- Write buffers allows buffering of ongoing TCP/IP packets, providing
- for higher performance, streamed output.
-
- The size of the write buffer will determine the maximum size of an
- outgoing TCP packet payload (MSS). This value should NOT exceed the
- maximum MSS which is determined by NET_BUFSIZE minus the size of
- TCP, IP, and Ethernet headers (assuming you are using the Ethernet
- transport). IPv4 hosts are required to be able to handle an MSS
- of 536 octets and IPv6 hosts are required to be able to handle an
- MSS of 1220 octets.
-
- This setting specifies the size of one TCP/IP write buffer. This
- should best be a equal to the maximum packet size (NET_BUFSIZE).
-
-config NET_NTCP_WRITE_BUFFERS
- int "Number of TCP/IP write buffers"
+config NET_TCP_NWRBCHAINS
+ int "Number of pre-allocated I/O buffer chain heads"
default 8
---help---
- Write buffers allows buffering of ongoing TCP/IP packets, providing
- for higher performance, streamed output.
-
- This setting specifies the number of TCP/IP write buffers.
+ These tiny nodes are used as "containers" to support queueing of
+ TCP write buffers. This setting will limit the number of TCP write
+ operations that can be "in-flight" at any give time. So a good
+ choice for this value would be the same as the maximum number of
+ TCP connections.
config NET_TCP_WRBUFFER_DEBUG
bool "Force write buffer debug"
diff --git a/nuttx/net/tcp/Make.defs b/nuttx/net/tcp/Make.defs
index fa1b24e46..b70b90ec9 100644
--- a/nuttx/net/tcp/Make.defs
+++ b/nuttx/net/tcp/Make.defs
@@ -51,6 +51,9 @@ endif
ifeq ($(CONFIG_NET_TCP_WRITE_BUFFERS),y)
NET_CSRCS += tcp_wrbuffer.c
+ifeq ($(CONFIG_DEBUG),y)
+NET_CSRCS += tcp_wrbuffer_dump.c
+endif
endif
# Include TCP build support
diff --git a/nuttx/net/tcp/tcp.h b/nuttx/net/tcp/tcp.h
index b2b515793..a18b78a6c 100644
--- a/nuttx/net/tcp/tcp.h
+++ b/nuttx/net/tcp/tcp.h
@@ -98,10 +98,8 @@ void tcp_wrbuffer_initialize(void);
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
struct tcp_wrbuffer_s;
-struct timespec;
-FAR struct tcp_wrbuffer_s *
-tcp_wrbuffer_alloc(FAR const struct timespec *abstime);
+FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void);
#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */
/****************************************************************************
@@ -118,7 +116,23 @@ tcp_wrbuffer_alloc(FAR const struct timespec *abstime);
****************************************************************************/
#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
-void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrbuffer);
+void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb);
+#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */
+
+/****************************************************************************
+ * Function: tcp_writebuffer_dump
+ *
+ * Description:
+ * Dump the contents of a write buffer.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
+#ifdef CONFIG_DEBUG
+void tcp_writebuffer_dump(FAR const char *msg, FAR struct tcp_wrbuffer_s *wrb);
+#else
+# define tcp_writebuffer_dump(msg,wrb)
+#endif
#endif /* CONFIG_NET_TCP_WRITE_BUFFERS */
#undef EXTERN
diff --git a/nuttx/net/tcp/tcp_wrbuffer.c b/nuttx/net/tcp/tcp_wrbuffer.c
index cba5f5d23..929b2f3a2 100644
--- a/nuttx/net/tcp/tcp_wrbuffer.c
+++ b/nuttx/net/tcp/tcp_wrbuffer.c
@@ -50,9 +50,13 @@
#include <queue.h>
#include <semaphore.h>
+#include <string.h>
+#include <assert.h>
#include <debug.h>
-#include "uip/uip_internal.h"
+#include "tcp/tcp.h"
+#include "nuttx/net/iob.h"
+#include "nuttx/net/uip/uip-tcp.h"
/****************************************************************************
* Private Types
@@ -72,7 +76,7 @@ struct wrbuffer_s
/* These are the pre-allocated write buffers */
- struct tcp_wrbuffer_s buffers[CONFIG_NET_NTCP_WRITE_BUFFERS];
+ struct tcp_wrbuffer_s buffers[CONFIG_NET_TCP_NWRBCHAINS];
};
/****************************************************************************
@@ -108,12 +112,12 @@ void tcp_wrbuffer_initialize(void)
sq_init(&g_wrbuffer.freebuffers);
- for (i = 0; i < CONFIG_NET_NTCP_WRITE_BUFFERS; i++)
+ for (i = 0; i < CONFIG_NET_TCP_NWRBCHAINS; i++)
{
sq_addfirst(&g_wrbuffer.buffers[i].wb_node, &g_wrbuffer.freebuffers);
}
- sem_init(&g_wrbuffer.sem, 0, CONFIG_NET_NTCP_WRITE_BUFFERS);
+ sem_init(&g_wrbuffer.sem, 0, CONFIG_NET_TCP_NWRBCHAINS);
}
/****************************************************************************
@@ -129,26 +133,39 @@ void tcp_wrbuffer_initialize(void)
*
****************************************************************************/
-FAR struct tcp_wrbuffer_s *
-tcp_wrbuffer_alloc(FAR const struct timespec *abstime)
+FAR struct tcp_wrbuffer_s *tcp_wrbuffer_alloc(void)
{
- int ret;
+ FAR struct tcp_wrbuffer_s *wrb;
- if (abstime)
- {
- ret = sem_timedwait(&g_wrbuffer.sem, abstime);
- }
- else
- {
- ret = sem_wait(&g_wrbuffer.sem);
- }
+ /* We need to allocate two things: (1) A write buffer structure and (2)
+ * at least one I/O buffer to start the chain.
+ *
+ * Allocate the write buffer structure first then the IOBG. In order to
+ * avoid deadlocks, we will need to free the IOB first, then the write
+ * buffer
+ */
+
+ DEBUGVERIFY(sem_wait(&g_wrbuffer.sem));
+
+ /* Now, we are guaranteed to have a write buffer structure reserved
+ * for us in the free list.
+ */
- if (ret != 0)
+ wrb = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&g_wrbuffer.freebuffers);
+ DEBUGASSERT(wrb);
+ memset(wrb, 0, sizeof(struct tcp_wrbuffer_s));
+
+ /* Now get the first I/O buffer for the write buffer structure */
+
+ wrb->wb_iob = iob_alloc();
+ if (!wrb->wb_iob)
{
+ ndbg("ERROR: Failed to allocate I/O buffer\n");
+ tcp_wrbuffer_release(wrb);
return NULL;
}
- return (FAR struct tcp_wrbuffer_s*)sq_remfirst(&g_wrbuffer.freebuffers);
+ return wrb;
}
/****************************************************************************
@@ -164,9 +181,19 @@ tcp_wrbuffer_alloc(FAR const struct timespec *abstime)
*
****************************************************************************/
-void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrbuffer)
+void tcp_wrbuffer_release(FAR struct tcp_wrbuffer_s *wrb)
{
- sq_addlast(&wrbuffer->wb_node, &g_wrbuffer.freebuffers);
+ DEBUGASSERT(wrb && wrb->wb_iob);
+
+ /* To avoid deadlocks, we must following this ordering: Release the I/O
+ * buffer chain first, then the write buffer structure.
+ */
+
+ iob_free_chain(wrb->wb_iob);
+
+ /* Then free the write buffer structure */
+
+ sq_addlast(&wrb->wb_node, &g_wrbuffer.freebuffers);
sem_post(&g_wrbuffer.sem);
}
diff --git a/nuttx/net/tcp/tcp_wrbuffer_dump.c b/nuttx/net/tcp/tcp_wrbuffer_dump.c
new file mode 100644
index 000000000..910eeb304
--- /dev/null
+++ b/nuttx/net/tcp/tcp_wrbuffer_dump.c
@@ -0,0 +1,89 @@
+/****************************************************************************
+ * net/tcp/tcp_wrbuffer_dump.c
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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
+ * COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stdint.h>
+#include <debug.h>
+
+#include <nuttx/net/iob.h>
+#include <nuttx/net/uip/uip-tcp.h>
+
+#ifdef CONFIG_DEBUG
+
+/****************************************************************************
+ * Pre-processor definitions
+ ****************************************************************************/
+
+/* Select the lowest level debug interface available */
+
+#ifdef CONFIG_CPP_HAVE_VARARGS
+# ifdef CONFIG_ARCH_LOWPUTC
+# define message(format, ...) lowsyslog(format, ##__VA_ARGS__)
+# else
+# define message(format, ...) syslog(format, ##__VA_ARGS__)
+# endif
+#else
+# ifdef CONFIG_ARCH_LOWPUTC
+# define message lowsyslog
+# else
+# define message syslog
+# endif
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: tcp_wrbuffer_dump
+ *
+ * Description:
+ * Dump the contents of a write buffer
+ *
+ ****************************************************************************/
+
+void tcp_wrbuffer_dump(FAR const char *msg, FAR struct tcp_wrbuffer_s *wrb)
+{
+ message("%s: WRB=%p segno=%d sent=%d nrtx=%d\n",
+ msg, wrb, WRB_SEQNO(wrb), WRB_SENT(wrb), WRB_NRTX(wrb));
+ iob_dump("I/O Buffer Chain", WRB_IOB(wrb));
+}
+
+#endif /* CONFIG_DEBUG */
diff --git a/nuttx/net/uip/Make.defs b/nuttx/net/uip/Make.defs
index 8849e32fe..6b780152d 100644
--- a/nuttx/net/uip/Make.defs
+++ b/nuttx/net/uip/Make.defs
@@ -40,6 +40,12 @@ ifeq ($(CONFIG_NET),y)
NET_CSRCS += uip_initialize.c uip_setipid.c uip_input.c uip_send.c
NET_CSRCS += uip_poll.c uip_chksum.c uip_callback.c
+# I/O buffer chain support required?
+
+ifeq ($(CONFIG_NET_IOB),y)
+NET_CSRCS += uip_iobsend.c
+endif
+
# Non-interrupt level support required?
ifeq ($(CONFIG_NET_NOINTS),y)
diff --git a/nuttx/net/uip/uip_iobsend.c b/nuttx/net/uip/uip_iobsend.c
new file mode 100644
index 000000000..d5ed4072b
--- /dev/null
+++ b/nuttx/net/uip/uip_iobsend.c
@@ -0,0 +1,105 @@
+/****************************************************************************
+ * net/uip/uip_iobsend.c
+ *
+ * Copyright (C) 2014 Gregory Nutt. All rights reserved.
+ * Author: Gregory Nutt <gnutt@nuttx.org>
+ *
+ * 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. Neither the name NuttX nor the names of its contributors may be
+ * used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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
+ * COPYRIGHT OWNER OR CONTRIBUTORS 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.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <string.h>
+#include <assert.h>
+#include <debug.h>
+
+#include <nuttx/net/iob.h>
+#include <nuttx/net/uip/uip.h>
+#include <nuttx/net/uip/uip-arch.h>
+
+#ifdef CONFIG_NET_IOB
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Type Declarations
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Global Constant Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Global Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Constant Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Private Variables
+ ****************************************************************************/
+
+/****************************************************************************
+ * Global Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: uip_iobsend
+ *
+ * Description:
+ * Called from socket logic in response to a xmit or poll request from the
+ * the network interface driver.
+ *
+ * Assumptions:
+ * Called from the interrupt level or, at a minimum, with interrupts
+ * disabled.
+ *
+ ****************************************************************************/
+
+void uip_iobsend(FAR struct uip_driver_s *dev, FAR struct iob_s *iob,
+ unsigned int len, unsigned int offset)
+{
+ DEBUGASSERT(dev && len > 0 && len < CONFIG_NET_BUFSIZE);
+
+ iob_copyout(dev->d_snddata, iob, len, offset);
+ dev->d_sndlen = len;
+}
+
+#endif /* CONFIG_NET_IOB */
+
diff --git a/nuttx/net/uip/uip_send.c b/nuttx/net/uip/uip_send.c
index 0b799f203..5a9015d89 100644
--- a/nuttx/net/uip/uip_send.c
+++ b/nuttx/net/uip/uip_send.c
@@ -1,7 +1,7 @@
/****************************************************************************
* net/uip/uip_send.c
*
- * Copyright (C) 2007i, 2008 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2007, 2008 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Based in part on uIP which also has a BSD stylie license:
@@ -42,6 +42,7 @@
****************************************************************************/
#include <string.h>
+#include <assert.h>
#include <debug.h>
#include <nuttx/net/uip/uip.h>
@@ -94,13 +95,8 @@
void uip_send(struct uip_driver_s *dev, const void *buf, int len)
{
- /* Some sanity checks -- note that the actually available length in the
- * buffer is considerably less than CONFIG_NET_BUFSIZE.
- */
+ DEBUGASSERT(dev && len > 0 && len < CONFIG_NET_BUFSIZE);
- if (dev && len > 0 && len < CONFIG_NET_BUFSIZE)
- {
- memcpy(dev->d_snddata, buf, len);
- dev->d_sndlen = len;
- }
+ memcpy(dev->d_snddata, buf, len);
+ dev->d_sndlen = len;
}