summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nuttx/ChangeLog4
-rw-r--r--nuttx/include/nuttx/net/iob.h31
-rw-r--r--nuttx/include/nuttx/net/uip/uip-arch.h2
-rw-r--r--nuttx/include/nuttx/net/uip/uip-tcp.h46
-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/Kconfig23
-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.c11
-rw-r--r--nuttx/net/iob/iob_alloc.c107
-rw-r--r--nuttx/net/iob/iob_alloc_qentry.c118
-rw-r--r--nuttx/net/iob/iob_clone.c7
-rw-r--r--nuttx/net/iob/iob_concat.c7
-rwxr-xr-xnuttx/net/iob/iob_contig.c7
-rw-r--r--nuttx/net/iob/iob_copyin.c17
-rw-r--r--nuttx/net/iob/iob_copyout.c7
-rw-r--r--nuttx/net/iob/iob_dump.c180
-rw-r--r--nuttx/net/iob/iob_free.c22
-rw-r--r--nuttx/net/iob/iob_free_chain.c27
-rw-r--r--nuttx/net/iob/iob_free_qentry.c20
-rw-r--r--nuttx/net/iob/iob_free_queue.c11
-rw-r--r--nuttx/net/iob/iob_initialize.c24
-rw-r--r--nuttx/net/iob/iob_pack.c7
-rw-r--r--nuttx/net/iob/iob_remove_queue.c10
-rw-r--r--nuttx/net/iob/iob_trimhead.c33
-rw-r--r--nuttx/net/iob/iob_trimtail.c11
-rw-r--r--nuttx/net/net_send_buffered.c544
-rw-r--r--nuttx/net/net_send_unbuffered.c1
-rw-r--r--nuttx/net/net_sockets.c9
-rw-r--r--nuttx/net/tcp/Kconfig47
-rw-r--r--nuttx/net/tcp/Make.defs3
-rw-r--r--nuttx/net/tcp/tcp.h23
-rw-r--r--nuttx/net/tcp/tcp_wrbuffer.c65
-rw-r--r--nuttx/net/tcp/tcp_wrbuffer_dump.c90
-rw-r--r--nuttx/net/uip/Make.defs6
-rw-r--r--nuttx/net/uip/uip_iobsend.c113
-rw-r--r--nuttx/net/uip/uip_send.c14
38 files changed, 1426 insertions, 265 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index d42079868..c2d5e808b 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -7461,6 +7461,10 @@
* tools/mkfsdata.pl: Apparently, the trailing the 0x00 in http server
files is seen as a bug in javascript and images. From Max/Himax
(2014-6-19).
+ * net/ and include/nutt/net (many files): Conversion of the TCP
+ write buffering logic to use I/O buffer chains (not tested on initial
+ check-in) (2014-6-22).
+
* include/time.h and sched/clock_gettime.c: Fix typo in conditional
compilation: CONFIG_CLOCK_MONOTONIC vs. CLOCK_MONOTONIC. From Manuel
Stühn (2014-6-23).
diff --git a/nuttx/include/nuttx/net/iob.h b/nuttx/include/nuttx/net/iob.h
index ac3977538..25dbbc39a 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,26 @@ 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, unsigned int len,
+ unsigned int offset);
+#else
+# define iob_dump(wrb)
+#endif
+
#endif /* _INCLUDE_NUTTX_NET_IOB_H */
diff --git a/nuttx/include/nuttx/net/uip/uip-arch.h b/nuttx/include/nuttx/net/uip/uip-arch.h
index c6b664d7f..905af0c50 100644
--- a/nuttx/include/nuttx/net/uip/uip-arch.h
+++ b/nuttx/include/nuttx/net/uip/uip-arch.h
@@ -163,7 +163,7 @@ struct uip_driver_s
uint16_t d_len;
- /* When d_buf contains outgoing xmit data, xmtlen is nonzero and represents
+ /* When d_buf contains outgoing xmit data, d_sndlen is nonzero and represents
* the amount of appllcation data after d_snddata
*/
diff --git a/nuttx/include/nuttx/net/uip/uip-tcp.h b/nuttx/include/nuttx/net/uip/uip-tcp.h
index cbb125a74..b1e0fac2f 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,28 @@
# 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) \
+ do { (wrb)->wb_iob = iob_trimhead((wrb)->wb_iob,(n)); } while (0)
+
+#ifdef CONFIG_DEBUG
+# define WRB_DUMP(msg,wrb,len,offset) \
+ tcp_wrbuffer_dump(msg,wrb,len,offset)
+#else
+# define WRB_DUMP(msg,wrb,len,offset)
+#endif
+#endif
+
/****************************************************************************
* Public Type Definitions
****************************************************************************/
@@ -131,7 +154,7 @@
*
* The uip_conn structure is used for identifying a connection. All
* but one field in the structure are to be considered read-only by an
- * application. The only exception is the "private: field whos purpose
+ * application. The only exception is the 'private' fields whose purpose
* is to let the application store application-specific state (e.g.,
* file pointers) for the connection.
*/
@@ -170,22 +193,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 (ACKed and un-ACKed) */
uint32_t isn; /* Initial sequence number */
#endif
@@ -261,14 +290,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..ea6a851cd 100644
--- a/nuttx/net/iob/Kconfig
+++ b/nuttx/net/iob/Kconfig
@@ -30,10 +30,27 @@ 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.
+
+config NET_IOB_DEBUG
+ bool "Force I/O buffer debug"
+ default n
+ depends on DEBUG
+ ---help---
+ This option will force debug output from I/O buffer logic,
+ even without network debug output. This is not normally something
+ that would want to do but is convenient if you are debugging the
+ I/O buffer logic and do not want to get overloaded with other
+ network-related debug output.
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..f915b49a0 100644
--- a/nuttx/net/iob/iob_add_queue.c
+++ b/nuttx/net/iob/iob_add_queue.c
@@ -39,6 +39,13 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <assert.h>
#include <errno.h>
#include <debug.h>
@@ -47,6 +54,8 @@
#include "iob.h"
+#if CONFIG_IOB_NCHAINS > 0
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -102,3 +111,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..be6491dbb 100644
--- a/nuttx/net/iob/iob_alloc.c
+++ b/nuttx/net/iob/iob_alloc.c
@@ -39,6 +39,16 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
+#include <semaphore.h>
+#include <assert.h>
+
#include <nuttx/arch.h>
#include <nuttx/net/iob.h>
@@ -61,18 +71,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 +96,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 +116,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..43813a269 100644
--- a/nuttx/net/iob/iob_alloc_qentry.c
+++ b/nuttx/net/iob/iob_alloc_qentry.c
@@ -39,11 +39,23 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
+#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 +73,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 +197,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 +213,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_clone.c b/nuttx/net/iob/iob_clone.c
index 9d4b849f8..ce83400d4 100644
--- a/nuttx/net/iob/iob_clone.c
+++ b/nuttx/net/iob/iob_clone.c
@@ -39,6 +39,13 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <string.h>
#include <assert.h>
#include <errno.h>
diff --git a/nuttx/net/iob/iob_concat.c b/nuttx/net/iob/iob_concat.c
index 6875984ff..68d35c480 100644
--- a/nuttx/net/iob/iob_concat.c
+++ b/nuttx/net/iob/iob_concat.c
@@ -39,6 +39,13 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <string.h>
#include <nuttx/net/iob.h>
diff --git a/nuttx/net/iob/iob_contig.c b/nuttx/net/iob/iob_contig.c
index ff9831a21..6af1b9c99 100755
--- a/nuttx/net/iob/iob_contig.c
+++ b/nuttx/net/iob/iob_contig.c
@@ -39,6 +39,13 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <string.h>
#include <assert.h>
#include <errno.h>
diff --git a/nuttx/net/iob/iob_copyin.c b/nuttx/net/iob/iob_copyin.c
index 0c9b3f037..fedd37098 100644
--- a/nuttx/net/iob/iob_copyin.c
+++ b/nuttx/net/iob/iob_copyin.c
@@ -39,6 +39,13 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <stdint.h>
#include <string.h>
#include <errno.h>
@@ -91,13 +98,14 @@ int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
unsigned int ncopy;
unsigned int avail;
+ nllvdbg("iob=%p len=%u offset=%u\n", iob, len, offset);
DEBUGASSERT(iob && src);
/* The offset must applied to data that is already in the I/O buffer chain */
if (offset > iob->io_pktlen)
{
- ndbg("ERROR: offset is past the end of data: %d > %d\n",
+ ndbg("ERROR: offset is past the end of data: %u > %u\n",
offset, iob->io_pktlen);
return -ESPIPE;
}
@@ -123,6 +131,8 @@ int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
dest = &iob->io_data[iob->io_offset + offset];
avail = iob->io_len - offset;
+ nllvdbg("iob=%p avail=%u len=%u next=%p\n", iob, avail, len, next);
+
/* Will the rest of the copy fit into this buffer, overwriting
* existing data.
*/
@@ -146,7 +156,7 @@ int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
/* Yes.. We can extend this buffer to the up to the very end. */
- maxlen = CONFIG_IOB_BUFSIZE - iob->io_offset;
+ maxlen = CONFIG_IOB_BUFSIZE - iob->io_offset;
/* This is the new buffer length that we need. Of course,
* clipped to the maximum possible size in this buffer.
@@ -178,6 +188,8 @@ int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
/* Copy from the user buffer to the I/O buffer. */
memcpy(dest, src, ncopy);
+ nllvdbg("iob=%p, Copy %u bytes, new len=%u\n",
+ iob, ncopy, iob->io_len);
/* Adjust the total length of the copy and the destination address in
* the user buffer.
@@ -204,6 +216,7 @@ int iob_copyin(FAR struct iob_s *iob, FAR const uint8_t *src,
/* Add the new, empty I/O buffer to the end of the buffer chain. */
iob->io_flink = next;
+ nllvdbg("iob=%p added to the chain\n", iob);
}
iob = next;
diff --git a/nuttx/net/iob/iob_copyout.c b/nuttx/net/iob/iob_copyout.c
index 5695eb2e9..2db050d31 100644
--- a/nuttx/net/iob/iob_copyout.c
+++ b/nuttx/net/iob/iob_copyout.c
@@ -39,6 +39,13 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <stdint.h>
#include <string.h>
#include <assert.h>
diff --git a/nuttx/net/iob/iob_dump.c b/nuttx/net/iob/iob_dump.c
new file mode 100644
index 000000000..ee7cda4e3
--- /dev/null
+++ b/nuttx/net/iob/iob_dump.c
@@ -0,0 +1,180 @@
+/****************************************************************************
+ * 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
+ ****************************************************************************/
+
+ #ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/* 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, unsigned int len,
+ unsigned int offset)
+{
+ FAR struct iob_s *head;
+ uint8_t data[32];
+ unsigned int maxlen;
+ unsigned int nbytes;
+ unsigned int lndx;
+ unsigned int cndx;
+
+ head = iob;
+ message("%s: iob=%p pktlen=%d\n", msg, head, head->io_pktlen);
+
+ /* Check if the offset is beyond the data in the I/O buffer chain */
+
+ if (offset > head->io_pktlen)
+ {
+ ndbg("ERROR: offset is past the end of data: %u > %u\n",
+ offset, head->io_pktlen);
+ return;
+ }
+
+ /* Dump I/O buffer headers */
+
+ for (; iob; iob = iob->io_flink)
+ {
+ message(" iob=%p len=%d offset=%d\n", iob, iob->io_len, iob->io_offset);
+ }
+
+ /* Get the amount of data to be displayed, limited by the amount that we
+ * have beyond the offset.
+ */
+
+ maxlen = head->io_pktlen - offset;
+ len = MIN(len, maxlen);
+
+ /* Then beginning printing with the buffer containing the offset in groups
+ * of 32 bytes.
+ */
+
+ for (lndx = 0; lndx < len; lndx += 32, offset += 32)
+ {
+ /* Copy 32-bytes into our local buffer from the current offset */
+
+ nbytes = iob_copyout(data, head, 32, offset);
+
+ /* Make sure that we have something to print */
+
+ if (nbytes > 0)
+ {
+ message(" %04x: ", offset);
+
+ for (cndx = 0; cndx < 32; cndx++)
+ {
+ if (cndx == 16)
+ {
+ message(" ");
+ }
+
+ if ((lndx + cndx) < len)
+ {
+ message("%02x", data[cndx]);
+ }
+ else
+ {
+ message(" ");
+ }
+ }
+
+ message(" ");
+ for (cndx = 0; cndx < 32; cndx++)
+ {
+ if (cndx == 16)
+ {
+ message(" ");
+ }
+
+ if ((lndx + cndx) < len)
+ {
+ if (data[cndx] >= 0x20 && data[cndx] < 0x7f)
+ {
+ message("%c", data[cndx]);
+ }
+ 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..68c7fb264 100644
--- a/nuttx/net/iob/iob_free.c
+++ b/nuttx/net/iob/iob_free.c
@@ -39,7 +39,16 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
+#include <semaphore.h>
#include <assert.h>
+#include <debug.h>
#include <nuttx/arch.h>
#include <nuttx/net/iob.h>
@@ -80,13 +89,15 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob)
FAR struct iob_s *next = iob->io_flink;
irqstate_t flags;
+ nllvdbg("iob=%p io_pktlen=%u io_len=%u next=%p\n",
+ iob, iob->io_pktlen, iob->io_len, next);
+
/* Copy the data that only exists in the head of a I/O buffer chain into
* the next entry.
*/
if (next)
{
-
/* Copy and decrement the total packet length, being careful to
* do nothing too crazy.
*/
@@ -107,6 +118,9 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob)
next->io_pktlen = 0;
DEBUGASSERT(next->io_len == 0 && next->io_flink == NULL);
}
+
+ nllvdbg("next=%p io_pktlen=%u io_len=%u\n",
+ next, next->io_pktlen, next->io_len);
}
/* Free the I/O buffer by adding it to the head of the free list. We don't
@@ -115,8 +129,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..d09a0ca97 100644
--- a/nuttx/net/iob/iob_free_chain.c
+++ b/nuttx/net/iob/iob_free_chain.c
@@ -39,6 +39,13 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <nuttx/arch.h>
#include <nuttx/net/iob.h>
@@ -75,20 +82,12 @@
void iob_free_chain(FAR struct iob_s *iob)
{
- FAR struct iob_s *last;
- irqstate_t flags;
-
- /* Find the last entry in the I/O buffer list */
-
- for (last = iob; last->io_flink; last = last->io_flink);
+ FAR struct iob_s *next;
- /* 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.
- */
+ /* Free each IOB in the chain -- one at a time to keep the count straight */
- 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..3f5a517b9 100644
--- a/nuttx/net/iob/iob_free_qentry.c
+++ b/nuttx/net/iob/iob_free_qentry.c
@@ -39,6 +39,14 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
+#include <semaphore.h>
#include <assert.h>
#include <nuttx/arch.h>
@@ -46,6 +54,8 @@
#include "iob.h"
+#if CONFIG_IOB_NCHAINS > 0
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -70,7 +80,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 +96,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..f23394d45 100644
--- a/nuttx/net/iob/iob_free_queue.c
+++ b/nuttx/net/iob/iob_free_queue.c
@@ -39,12 +39,21 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <assert.h>
#include <nuttx/net/iob.h>
#include "iob.h"
+#if CONFIG_IOB_NCHAINS > 0
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -112,3 +121,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..319e75b8a 100644
--- a/nuttx/net/iob/iob_initialize.c
+++ b/nuttx/net/iob/iob_initialize.c
@@ -39,7 +39,15 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <stdbool.h>
+#include <semaphore.h>
#include <nuttx/net/iob.h>
@@ -60,7 +68,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 +82,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 +126,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 +141,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_pack.c b/nuttx/net/iob/iob_pack.c
index eb2c772c4..f129e7357 100644
--- a/nuttx/net/iob/iob_pack.c
+++ b/nuttx/net/iob/iob_pack.c
@@ -39,6 +39,13 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <string.h>
#include <nuttx/net/iob.h>
diff --git a/nuttx/net/iob/iob_remove_queue.c b/nuttx/net/iob/iob_remove_queue.c
index 7a64d47c7..e8ee794e3 100644
--- a/nuttx/net/iob/iob_remove_queue.c
+++ b/nuttx/net/iob/iob_remove_queue.c
@@ -39,12 +39,21 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <debug.h>
#include <nuttx/net/iob.h>
#include "iob.h"
+#if CONFIG_IOB_NCHAINS > 0
+
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
@@ -92,3 +101,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/iob/iob_trimhead.c b/nuttx/net/iob/iob_trimhead.c
index 0fe349023..6ea684b9a 100644
--- a/nuttx/net/iob/iob_trimhead.c
+++ b/nuttx/net/iob/iob_trimhead.c
@@ -39,7 +39,15 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <assert.h>
+#include <debug.h>
#include <nuttx/net/iob.h>
@@ -81,20 +89,22 @@
FAR struct iob_s *iob_trimhead(FAR struct iob_s *iob, unsigned int trimlen)
{
uint16_t pktlen;
- unsigned int len;
+
+ nllvdbg("iob=%p trimlen=%d\n", iob, trimlen);
if (iob && trimlen > 0)
{
/* Trim from the head of the I/IO buffer chain */
pktlen = iob->io_pktlen;
- len = trimlen;
-
- while (len > 0 && iob != NULL)
+ while (trimlen > 0 && iob != NULL)
{
/* Do we trim this entire I/O buffer away? */
- if (iob->io_len <= len)
+ nllvdbg("iob=%p io_len=%d pktlen=%d trimlen=%d\n",
+ iob, iob->io_len, pktlen, trimlen);
+
+ if (iob->io_len <= trimlen)
{
FAR struct iob_s *next;
@@ -103,7 +113,7 @@ FAR struct iob_s *iob_trimhead(FAR struct iob_s *iob, unsigned int trimlen)
*/
pktlen -= iob->io_len;
- len -= iob->io_len;
+ trimlen -= iob->io_len;
iob->io_len = 0;
iob->io_offset = 0;
@@ -122,8 +132,9 @@ FAR struct iob_s *iob_trimhead(FAR struct iob_s *iob, unsigned int trimlen)
/* Free this entry and set the next I/O buffer as the head */
+ nllvdbg("iob=%p: Freeing\n", iob);
(void)iob_free(iob);
- iob = next;
+ iob = next;
}
else
{
@@ -131,10 +142,10 @@ FAR struct iob_s *iob_trimhead(FAR struct iob_s *iob, unsigned int trimlen)
* stop the trim.
*/
- pktlen -= len;
- iob->io_len -= len;
- iob->io_offset += len;
- len = 0;
+ pktlen -= trimlen;
+ iob->io_len -= trimlen;
+ iob->io_offset += trimlen;
+ trimlen = 0;
}
}
diff --git a/nuttx/net/iob/iob_trimtail.c b/nuttx/net/iob/iob_trimtail.c
index 872560a49..c87de712f 100644
--- a/nuttx/net/iob/iob_trimtail.c
+++ b/nuttx/net/iob/iob_trimtail.c
@@ -39,7 +39,15 @@
#include <nuttx/config.h>
+#if defined(CONFIG_DEBUG) && defined(CONFIG_NET_IOB_DEBUG)
+/* Force debug output (from this file only) */
+
+# undef CONFIG_DEBUG_NET
+# define CONFIG_DEBUG_NET 1
+#endif
+
#include <string.h>
+#include <debug.h>
#include <nuttx/net/iob.h>
@@ -81,6 +89,8 @@ FAR struct iob_s *iob_trimtail(FAR struct iob_s *iob, unsigned int trimlen)
unsigned int iosize;
int len;
+ nlldbg("iob=%p pktlen=%d trimlen=%d\n", iob, iob->io_pktlen, trimlen);
+
if (iob && trimlen > 0)
{
len = trimlen;
@@ -113,6 +123,7 @@ FAR struct iob_s *iob_trimtail(FAR struct iob_s *iob, unsigned int trimlen)
* I/O buffer away?
*/
+ nllvdbg("iob=%p len=%d vs %d\n", last, last->io_len, len);
if (last->io_len <= len)
{
/* Yes.. Consume the entire buffer */
diff --git a/nuttx/net/net_send_buffered.c b/nuttx/net/net_send_buffered.c
index 3717b609b..639b177ca 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"
@@ -76,6 +77,16 @@
#define TCPBUF ((struct uip_tcpip_hdr *)&dev->d_buf[UIP_LLH_LEN])
+/* Debug */
+
+#ifdef CONFIG_NET_TCP_WRBUFFER_DUMP
+# define BUF_DUMP(msg,buf,len) lib_dumpbuffer(msg,buf,len)
+#else
+# define BUF_DUMP(msg,buf,len,offset)
+# undef WRB_DUMP
+# define WRB_DUMP(msg,wrb,len,offset)
+#endif
+
/****************************************************************************
* Private Types
****************************************************************************/
@@ -92,8 +103,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 +114,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 +145,53 @@ 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->sent = 0;
+}
+
+/****************************************************************************
* Function: send_interrupt
*
* Description:
@@ -156,7 +214,7 @@ 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);
@@ -167,66 +225,257 @@ 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=%u flags=%04x\n", ackno, flags);
+
+ /* Look at every write buffer in the 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 (segment->wb_seqno < ackno)
+ /* 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 (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: wrb=%p seqno=%u lastseq=%u pktlen=%u ackno=%u\n",
+ wrb, WRB_SEQNO(wrb), lastseq, WRB_PKTLEN(wrb), ackno);
+
+ /* Has the entire buffer been ACKed? */
+
+ if (ackno >= lastseq)
+ {
+ nllvdbg("ACK: wrb=%p Freeing write buffer\n", wrb);
+
+ /* 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 */
- /* Segment was ACKed. Remove from ACK waiting queue */
+ tcp_wrbuffer_release(wrb);
+ }
+ else
+ {
+ unsigned int trimlen;
+
+ /* 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.
+ */
+
+ trimlen = ackno - WRB_SEQNO(wrb);
+ if (trimlen > WRB_SENT(wrb))
+ {
+ /* More data has been ACKed then we have sent? */
+
+ trimlen = WRB_SENT(wrb);
+ }
- sq_rem(entry, &conn->unacked_q);
+ nllvdbg("ACK: wrb=%p trim %u bytes\n", wrb, trimlen);
- /* Return the write buffer to the pool of free buffers */
+ WRB_TRIM(wrb, trimlen);
+ WRB_SEQNO(wrb) = ackno;
+ WRB_SENT(wrb) -= trimlen;
- tcp_wrbuffer_release(segment);
+ /* Set the new sequence number for what remains */
+
+ nllvdbg("ACK: wrb=%p seqno=%u pktlen=%u\n",
+ wrb, 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;
+
+ /* Number of bytes that were ACKed */
+
+ nacked = ackno - WRB_SEQNO(wrb);
+ if (nacked > WRB_SENT(wrb))
+ {
+ /* More data has been ACKed then we have sent? ASSERT? */
+
+ nacked = WRB_SENT(wrb);
+ }
+
+ nllvdbg("ACK: wrb=%p seqno=%u nacked=%u sent=%u ackno=%u\n",
+ wrb, 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: wrb=%p seqno=%u pktlen=%u sent=%u\n",
+ wrb, WRB_SEQNO(wrb), WRB_PKTLEN(wrb), WRB_SENT(wrb));
+ }
}
/* Check for a loss of connection */
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;
+
+ nllvdbg("REXMIT: %04x\n", flags);
+
+ /* If there is a partially sent write buffer at the head of the
+ * write_q? Has anything been sent from that write buffer?
+ */
+
+ wrb = (FAR struct tcp_wrbuffer_s *)sq_peek(&conn->write_q);
+ nllvdbg("REXMIT: wrb=%p sent=%u\n", wrb, wrb ? WRB_SENT(wrb) : 0);
+
+ if (wrb != NULL && WRB_SENT(wrb) > 0)
+ {
+ FAR struct tcp_wrbuffer_s *tmp;
+ uint16_t sent;
+
+ /* Yes.. Reset the number of bytes sent sent from the write buffer */
+
+ sent = WRB_SENT(wrb);
+ if (conn->unacked > sent)
+ {
+ conn->unacked -= sent;
+ }
+ else
+ {
+ conn->unacked = 0;
+ }
+
+ if (conn->sent > sent)
+ {
+ conn->sent -= sent;
+ }
+ else
+ {
+ conn->sent = 0;
+ }
+
+ WRB_SENT(wrb) = 0;
+ nllvdbg("REXMIT: wrb=%p sent=%u, conn unacked=%d sent=%d\n",
+ wrb, WRB_SENT(wrb), conn->unacked, conn->sent);
+
+ /* Increment the retransmit count on this write buffer. */
+
+ if (++WRB_NRTX(wrb) >= UIP_MAXRTX)
+ {
+ nlldbg("Expiring wrb=%p nrtx=%u\n", wrb, WRB_NRTX(wrb));
+
+ /* The maximum retry count as been exhausted. Remove the write
+ * buffer at the head of the queue.
+ */
+
+ tmp = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
+ DEBUGASSERT(tmp == wrb);
+ UNUSED(wrb);
+
+ /* And return the write buffer to the free list */
+
+ tcp_wrbuffer_release(wrb);
- /* 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.
+ /* NOTE expired is different from un-ACKed, it is designed to
+ * represent the number of segments that have been sent,
+ * retransmitted, and un-ACKed, if expired is not zero, the
+ * connection will be closed.
+ *
+ * field expired can only be updated at UIP_ESTABLISHED state
+ */
+
+ conn->expired++;
+ }
+ }
+
+ /* 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.
*/
- while ((entry = sq_remlast(&conn->unacked_q)))
+ while ((entry = sq_remlast(&conn->unacked_q)) != NULL)
{
- struct tcp_wrbuffer_s *segment = (struct tcp_wrbuffer_s*)entry;
+ wrb = (FAR struct tcp_wrbuffer_s*)entry;
+ uint16_t sent;
- if (segment->wb_nrtx >= UIP_MAXRTX)
+ /* Reset the number of bytes sent sent from the write buffer */
+
+ sent = WRB_SENT(wrb);
+ if (conn->unacked > sent)
+ {
+ conn->unacked -= sent;
+ }
+ else
+ {
+ conn->unacked = 0;
+ }
+
+ if (conn->sent > sent)
+ {
+ conn->sent -= sent;
+ }
+ else
+ {
+ conn->sent = 0;
+ }
+
+ WRB_SENT(wrb) = 0;
+ nllvdbg("REXMIT: wrb=%p sent=%u, conn unacked=%d sent=%d\n",
+ wrb, WRB_SENT(wrb), conn->unacked, conn->sent);
+
+ /* Free any write buffers that have exceed the retry count */
+
+ if (++WRB_NRTX(wrb) >= UIP_MAXRTX)
{
- //conn->unacked -= segment->wb_nbytes;
+ nlldbg("Expiring wrb=%p nrtx=%u\n", wrb, WRB_NRTX(wrb));
- /* Return the write buffer */
+ /* 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 +488,18 @@ 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.
+ */
+
+ nllvdbg("REXMIT: Moving wrb=%p nrtx=%u\n", wrb, WRB_NRTX(wrb));
- send_insert_seqment(segment, &conn->write_q);
+ send_insert_seqment(wrb, &conn->write_q);
+ }
}
}
@@ -259,7 +518,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,93 +542,122 @@ 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). We know from the above test that
+ * the write_q is not empty.
+ */
+
+ wrb = (FAR struct tcp_wrbuffer_s *)sq_peek(&conn->write_q);
+ DEBUGASSERT(wrb);
+
+ /* 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.
+ */
+
+ sndlen = WRB_PKTLEN(wrb) - WRB_SENT(wrb);
+ if (sndlen > uip_mss(conn))
+ {
+ sndlen = uip_mss(conn);
+ }
- segment = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
- if (segment)
+ if (sndlen > conn->winsize)
{
- sndbuf = segment->wb_buffer;
- sndlen = segment->wb_nbytes;
+ sndlen = conn->winsize;
+ }
+
+ nllvdbg("SEND: wrb=%p pktlen=%u sent=%u sndlen=%u\n",
+ wrb, WRB_PKTLEN(wrb), WRB_SENT(wrb), sndlen);
- DEBUGASSERT(sndlen <= uip_mss(conn));
+ /* Is this the first we have tried to send from this
+ * write buffer?
+ */
- /* REVISIT: There should be a check here to assure that we do
- * not excced the window (conn->winsize).
+ if (WRB_SENT(wrb) == 0)
+ {
+ /* Yes.. Set the sequence number for this segment. If
+ * we are retransmitting, then the sequence number will
+ * already be set for this write buffer.
*/
+
+ if (WRB_SEQNO(wrb) == (unsigned)-1)
+ {
+ WRB_SEQNO(wrb) = conn->isn + conn->sent;
+ }
- /* 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
+ /* 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.
*/
- if (segment->wb_nrtx == 0 && segment->wb_seqno == (unsigned)-1)
- {
- segment->wb_seqno = conn->isn + conn->sent;
- }
+ uip_tcpsetsequence(conn->sndseq, WRB_SEQNO(wrb));
+ }
- uip_tcpsetsequence(conn->sndseq, segment->wb_seqno);
+ /* 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).
+ */
- /* Then set-up to send that amount of data. (this won't
- * actually happen until the polling cycle completes).
- */
+ uip_iobsend(dev, WRB_IOB(wrb), sndlen, WRB_SENT(wrb));
- uip_send(dev, sndbuf, sndlen);
+ /* Remember how much data we send out now so that we know
+ * when everything has been acknowledged. Just increment
+ * the amount of data sent. This will be needed in
+ * sequence number calculations.
+ */
- /* Remember how much data we send out now so that we know
- * when everything has been acknowledged. Just increment
- * the amount of data sent. This will be needed in
- * sequence* number calculations and we know that this is
- * not a re-transmission. Re-transmissions do not go through
- * this path.
- */
+ conn->unacked += sndlen;
+ conn->sent += sndlen;
- if (segment->wb_nrtx == 0)
- {
- conn->unacked += sndlen;
- conn->sent += sndlen;
- }
+ nllvdbg("SEND: wrb=%p nrtx=%u unacked=%u sent=%u\n",
+ wrb, WRB_NRTX(wrb), conn->unacked, conn->sent);
- /* Increment the retransmission counter before expiration.
- * NOTE we will not calculate the retransmission timer
- * (RTT) to save cpu cycles, each send_insert_seqment
- * segment will be retransmitted UIP_MAXRTX times in halt-
- * second interval before expiration.
- */
+ /* Increment the count of bytes sent from this write buffer */
+
+ WRB_SENT(wrb) += sndlen;
+
+ nllvdbg("SEND: wrb=%p sent=%u pktlen=%u\n",
+ wrb, WRB_SENT(wrb), WRB_PKTLEN(wrb));
- segment->wb_nrtx++;
+ /* Remove the write buffer from the write queue if the
+ * last of the data has been sent from the buffer.
+ */
- /* The segment is waiting for ACK again */
+ DEBUGASSERT(WRB_SENT(wrb) <= WRB_PKTLEN(wrb));
+ if (WRB_SENT(wrb) >= WRB_PKTLEN(wrb))
+ {
+ FAR struct tcp_wrbuffer_s *tmp;
+
+ nllvdbg("ACK: wrb=%p Move to unacked_q\n", wrb);
- send_insert_seqment(segment, &conn->unacked_q);
+ tmp = (FAR struct tcp_wrbuffer_s *)sq_remfirst(&conn->write_q);
+ DEBUGASSERT(tmp == wrb);
+ UNUSED(tmp);
- /* Only one data can be sent by low level driver at once,
- * tell the caller stop polling the other connection.
+ /* Put the I/O buffer chain in the un-acked queue; the
+ * segment is waiting for ACK again
*/
- flags &= ~UIP_POLL;
+ 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.
+ */
+
+ flags &= ~UIP_POLL;
}
}
/* 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 +732,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;
@@ -460,6 +748,10 @@ ssize_t psock_send(FAR struct socket *psock, FAR const void *buf, size_t len,
goto errout;
}
+ /* Dump the incoming buffer */
+
+ BUF_DUMP("psock_send", buf, len);
+
/* Set the socket state to sending */
psock->s_flags = _SS_SETSTATE(psock->s_flags, _SF_SEND);
@@ -477,16 +769,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;
+
/* Set up the callback in the connection */
psock->s_sndcb->flags = (UIP_ACKDATA | UIP_REXMIT |UIP_POLL | \
@@ -494,47 +789,42 @@ 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)
+ /* Allocate an write buffer */
+
+ wrb = tcp_wrbuffer_alloc();
+ if (wrb)
{
- FAR struct tcp_wrbuffer_s *segment = tcp_wrbuffer_alloc(NULL);
- if (segment)
- {
- size_t cnt;
+ /* Initialize the write buffer */
- segment->wb_seqno = (unsigned)-1;
- segment->wb_nrtx = 0;
+ WRB_SEQNO(wrb) = (unsigned)-1;
+ WRB_NRTX(wrb) = 0;
+ WRB_COPYIN(wrb, (FAR uint8_t *)buf, len);
- if (len - completed > CONFIG_NET_TCP_WRITE_BUFSIZE)
- {
- cnt = CONFIG_NET_TCP_WRITE_BUFSIZE;
- }
- else
- {
- cnt = len - completed;
- }
+ /* Dump I/O buffer chain */
- segment->wb_nbytes = cnt;
- memcpy(segment->wb_buffer, (char*)buf + completed, cnt);
- completed += cnt;
+ WRB_DUMP("I/O buffer chain", wrb, WRB_PKTLEN(wrb), 0);
- /* 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);
+ nvdbg("Queued WRB=%p pktlen=%u write_q(%p,%p)\n",
+ wrb, WRB_PKTLEN(wrb),
+ conn->write_q.head, conn->write_q.tail);
- /* 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 +839,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 +857,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/net_sockets.c b/nuttx/net/net_sockets.c
index 5b3581c86..c6962915d 100644
--- a/nuttx/net/net_sockets.c
+++ b/nuttx/net/net_sockets.c
@@ -1,7 +1,7 @@
/****************************************************************************
* net/net_sockets.c
*
- * Copyright (C) 2007-2009, 2011-2013 Gregory Nutt. All rights reserved.
+ * Copyright (C) 2007-2009, 2011-2014 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@@ -47,6 +47,7 @@
#include <errno.h>
#include <debug.h>
+#include <nuttx/net/iob.h>
#include <nuttx/net/arp.h>
#include <nuttx/net/uip/uip.h>
#include <nuttx/net/net.h>
@@ -105,6 +106,12 @@ void net_initialize(void)
uip_initialize();
+ /* Initialize I/O buffering */
+
+#ifdef CONFIG_NET_IOB
+ iob_initialize();
+#endif
+
#ifdef CONFIG_NET_ROUTE
/* Initialize the routing table */
diff --git a/nuttx/net/tcp/Kconfig b/nuttx/net/tcp/Kconfig
index 1d41e762a..5989d3d90 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,46 +92,38 @@ 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"
default n
depends on DEBUG
+ select NET_IOB_DEBUG
---help---
This option will force debug output from TCP write buffer logic,
- even with network debug output. This is not normally something
+ even without network debug output. This is not normally something
that would want to do but is convenient if you are debugging the
write buffer logic and do not want to get overloaded with other
network-related debug output.
+config NET_TCP_WRBUFFER_DUMP
+ bool "Force write buffer dump"
+ default n
+ depends on DEBUG_NET || NET_TCP_WRBUFFER_DEBUG
+ select NET_IOB_DEBUG
+ ---help---
+ Dump the contents of the write buffers. You do not want to do this
+ unless you really want to analyze the write buffer transfers in
+ detail.
+
endif # NET_TCP_WRITE_BUFFERS
config NET_TCP_RECVDELAY
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..c776e7e47 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,24 @@ 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_wrbuffer_dump
+ *
+ * Description:
+ * Dump the contents of a write buffer.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_NET_TCP_WRITE_BUFFERS
+#ifdef CONFIG_DEBUG
+void tcp_wrbuffer_dump(FAR const char *msg, FAR struct tcp_wrbuffer_s *wrb,
+ unsigned int len, unsigned int offset);
+#else
+# define tcp_wrbuffer_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..6ff5bb021
--- /dev/null
+++ b/nuttx/net/tcp/tcp_wrbuffer_dump.c
@@ -0,0 +1,90 @@
+/****************************************************************************
+ * 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,
+ unsigned int len, unsigned int offset)
+{
+ 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), len, offset);
+}
+
+#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..c3bec5f99
--- /dev/null
+++ b/nuttx/net/uip/uip_iobsend.c
@@ -0,0 +1,113 @@
+/****************************************************************************
+ * 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);
+
+ /* Copy the data from the I/O buffer chain to the device buffer */
+
+ iob_copyout(dev->d_snddata, iob, len, offset);
+ dev->d_sndlen = len;
+
+#ifdef CONFIG_NET_TCP_WRBUFFER_DUMP
+ /* Dump the outgoing device buffer */
+
+ lib_dumpbuffer("uip_iobsend", dev->d_snddata, len);
+#endif
+}
+
+#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;
}