Commit 094c6505 authored by Andy Ross's avatar Andy Ross Committed by Jukka Rissanen
Browse files

net: tcp: TCP transmission



Hook up TCP transmission through the net_context_send() API.  Queues
packets via a list in the net_buf header, but as of right now simply
transmits the queue synchronously, ignoring the need for retransmit
and the limits of the receive window on the other side.

Requires that the ACK transmission be moved ahead of the net_context
callback invokation.  This to work around a glitch in the way ACKs
work with queueing (they depend on current state, but packets are
assembled just once) that will be fixed in a coming patch.

Change-Id: I7490333e4b314e7734fcc03f2a63d76ae89d698a
Signed-off-by: default avatarAndy Ross <andrew.j.ross@intel.com>
Signed-off-by: default avatarLeandro Pereira <leandro.pereira@intel.com>
parent 74c3b9a3
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -396,6 +396,9 @@ struct net_buf {
		struct net_buf *frags;
	};

	/** List pointer used for TCP retransmit buffering */
	sys_snode_t sent_list;

	/** Size of the user data associated with this buffer. */
	const uint16_t user_data_size;

+18 −20
Original line number Diff line number Diff line
@@ -1428,24 +1428,34 @@ static int send_data(struct net_context *context,
		     void *token,
		     void *user_data)
{
	int ret;

	context->send_cb = cb;
	context->user_data = user_data;
	net_nbuf_set_token(buf, token);

	ret =  net_send_data(buf);

	if (!timeout || net_context_get_ip_proto(context) == IPPROTO_UDP) {
		return ret;
		return net_send_data(buf);
	}

#if defined(CONFIG_NET_TCP)
	/* FIXME - Add TCP support here */
#endif
	if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
		int ret = tcp_send_data(context);

		/* Just make the callback synchronously even if it didn't
		 * go over the wire.  In theory it would be nice to track
		 * specific ACK locations in the stream and make the
		 * callback at that time, but there's nowhere to store the
		 * potentially-separate token/user_data values right now.
		 */
		if (cb) {
			cb(context, ret, token, user_data);
		}

		return ret;
	}
#endif

	return -EPROTONOSUPPORT;
}

/* If the reserve has changed, we need to adjust it accordingly in the
 * fragment chain. This can only happen in IEEE 802.15.4 where the link
@@ -1553,18 +1563,6 @@ static int create_udp_packet(struct net_context *context,
}
#endif /* CONFIG_NET_UDP */

#if defined(CONFIG_NET_TCP)
static int create_tcp_packet(struct net_context *context,
			     struct net_buf *buf,
			     const struct sockaddr *dst_addr,
			     struct net_buf **out_buf)
{
	NET_ASSERT(context->tcp);

	return 0;
}
#endif /* CONFIG_NET_TCP */

static int sendto(struct net_buf *buf,
		  const struct sockaddr *dst_addr,
		  socklen_t addrlen,
@@ -1648,7 +1646,7 @@ static int sendto(struct net_buf *buf,

#if defined(CONFIG_NET_TCP)
	if (net_context_get_ip_proto(context) == IPPROTO_TCP) {
		ret = create_tcp_packet(context, buf, dst_addr, &buf);
		ret = tcp_queue_data(context, buf);
	} else
#endif /* CONFIG_NET_TCP */
	{
+45 −1
Original line number Diff line number Diff line
@@ -365,7 +365,7 @@ int net_tcp_prepare_segment(struct net_tcp *tcp, uint8_t flags,
	segment.options = options;
	segment.optlen = optlen;

	*send_buf = prepare_segment(tcp, &segment, NULL);
	*send_buf = prepare_segment(tcp, &segment, *send_buf);

	tcp->send_seq = seq;

@@ -550,6 +550,50 @@ const char const *net_tcp_state_str(enum net_tcp_state state)
	return "";
}

int tcp_queue_data(struct net_context *context, struct net_buf *buf)
{
	int ret, data_len;
	struct net_conn *conn = (struct net_conn *)context->conn_handler;

	data_len = net_buf_frags_len(buf);

	/* Set PSH on all packets, our window is so small that there's
	 * no point in the remote side trying to finesse things and
	 * coalesce packets.
	 */
	ret = net_tcp_prepare_segment(context->tcp, NET_TCP_PSH | NET_TCP_ACK,
				      NULL, 0, &conn->remote_addr, &buf);
	if (ret) {
		return ret;
	}

	context->tcp->send_seq += data_len;

	sys_slist_append(&context->tcp->sent_list, &buf->sent_list);
	net_buf_ref(buf);

	return 0;
}

int tcp_send_data(struct net_context *context)
{
	struct net_buf *buf;
	sys_snode_t *node;

	/* For now, just send all queued data synchronously.  Need to
	 * add window handling and retry/ACK logic.
	 */
	SYS_SLIST_FOR_EACH_NODE(&context->tcp->sent_list, node) {
		buf = CONTAINER_OF(node, struct net_buf, sent_list);
		net_send_data(buf);
		net_nbuf_unref(buf);
	}

	sys_slist_init(&context->tcp->sent_list);

	return 0;
}

void net_tcp_init(void)
{
	k_sem_init(&tcp_lock, 0, UINT_MAX);
+22 −0
Original line number Diff line number Diff line
@@ -114,6 +114,9 @@ struct net_tcp {
	/** Active close timer */
	struct k_delayed_work fin_timer;

	/** List pointer used for TCP retransmit buffering */
	sys_slist_t sent_list;

	/** Highest acknowledged number of sent segments. */
	uint32_t recv_ack;

@@ -261,6 +264,25 @@ typedef void (*net_tcp_cb_t)(struct net_tcp *tcp, void *user_data);
 */
void net_tcp_foreach(net_tcp_cb_t cb, void *user_data);

/**
 * @brief Send available queued data over TCP connection
 *
 * @param context TCP context
 *
 * @return 0 if ok, < 0 if error
 */
int tcp_send_data(struct net_context *context);

/**
 * @brief Enqueue a single packet for transmission
 *
 * @param context TCP context
 * @param buf Packet
 *
 * @return 0 if ok, < 0 if error
 */
int tcp_queue_data(struct net_context *context, struct net_buf *buf);

#if defined(CONFIG_NET_TCP)
void net_tcp_init(void);
#else