Index: uspace/srv/net/tcp/Makefile
===================================================================
--- uspace/srv/net/tcp/Makefile	(revision 99ea91b21f443b2767081c1f285807bb248b0682)
+++ uspace/srv/net/tcp/Makefile	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -37,4 +37,5 @@
 	rqueue.c \
 	segment.c \
+	service.c \
 	seq_no.c \
 	tcp.c \
Index: uspace/srv/net/tcp/conn.c
===================================================================
--- uspace/srv/net/tcp/conn.c	(revision 99ea91b21f443b2767081c1f285807bb248b0682)
+++ uspace/srv/net/tcp/conn.c	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -121,5 +121,5 @@
 	fibril_condvar_initialize(&conn->cstate_cv);
 
-	conn->cstate_cb = NULL;
+	conn->cb = NULL;
 
 	conn->cstate = st_listen;
@@ -275,7 +275,7 @@
 
 	/* Run user callback function */
-	if (conn->cstate_cb != NULL) {
+	if (conn->cb != NULL && conn->cb->cstate_change != NULL) {
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_state_set() - run user CB");
-		conn->cstate_cb(conn, conn->cstate_cb_arg);
+		conn->cb->cstate_change(conn, conn->cb_arg);
 	} else {
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_state_set() - no user CB");
@@ -1007,4 +1007,6 @@
 	/* Signal to the receive function that new data has arrived */
 	fibril_condvar_broadcast(&conn->rcv_buf_cv);
+	if (conn->cb != NULL && conn->cb->recv_data != NULL)
+		conn->cb->recv_data(conn, conn->cb_arg);
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
@@ -1098,4 +1100,6 @@
 		conn->rcv_buf_fin = true;
 		fibril_condvar_broadcast(&conn->rcv_buf_cv);
+		if (conn->cb != NULL && conn->cb->recv_data != NULL)
+			conn->cb->recv_data(conn, conn->cb_arg);
 
 		tcp_segment_delete(seg);
Index: uspace/srv/net/tcp/service.c
===================================================================
--- uspace/srv/net/tcp/service.c	(revision 779541be88a7692182b80bde3b7fae49f3832431)
+++ uspace/srv/net/tcp/service.c	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -0,0 +1,819 @@
+/*
+ * Copyright (c) 2015 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup udp
+ * @{
+ */
+
+/**
+ * @file HelenOS service implementation
+ */
+
+#include <async.h>
+#include <errno.h>
+#include <inet/endpoint.h>
+#include <inet/inet.h>
+#include <io/log.h>
+#include <ipc/services.h>
+#include <ipc/tcp.h>
+#include <loc.h>
+#include <macros.h>
+#include <stdlib.h>
+
+#include "conn.h"
+#include "service.h"
+#include "tcp_type.h"
+#include "ucall.h"
+
+#define NAME "tcp"
+
+#define MAX_MSG_SIZE DATA_XFER_LIMIT
+
+static void tcp_ev_data(tcp_cconn_t *);
+
+static void tcp_service_cstate_change(tcp_conn_t *, void *);
+static void tcp_service_recv_data(tcp_conn_t *, void *);
+
+static tcp_cb_t tcp_service_cb = {
+	.cstate_change = tcp_service_cstate_change,
+	.recv_data = tcp_service_recv_data
+};
+
+static void tcp_service_cstate_change(tcp_conn_t *conn, void *arg)
+{
+}
+
+static void tcp_service_recv_data(tcp_conn_t *conn, void *arg)
+{
+	tcp_cconn_t *cconn = (tcp_cconn_t *)arg;
+
+	tcp_ev_data(cconn);
+}
+
+static void tcp_ev_data(tcp_cconn_t *cconn)
+{
+	async_exch_t *exch;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_ev_data()");
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "client=%p\n", cconn->client);
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "sess=%p\n", cconn->client->sess);
+
+	exch = async_exchange_begin(cconn->client->sess);
+	aid_t req = async_send_1(exch, TCP_EV_DATA, cconn->id, NULL);
+	async_exchange_end(exch);
+
+	async_forget(req);
+}
+
+static int tcp_cconn_create(tcp_client_t *client, tcp_conn_t *conn,
+    tcp_cconn_t **rcconn)
+{
+	tcp_cconn_t *cconn;
+	sysarg_t id;
+
+	cconn = calloc(1, sizeof(tcp_cconn_t));
+	if (cconn == NULL)
+		return ENOMEM;
+
+	/* Allocate new ID */
+	id = 0;
+	list_foreach (client->cconn, lclient, tcp_cconn_t, cconn) {
+		if (cconn->id >= id)
+			id = cconn->id + 1;
+	}
+
+	cconn->id = id;
+	cconn->client = client;
+	cconn->conn = conn;
+
+	list_append(&cconn->lclient, &client->cconn);
+	*rcconn = cconn;
+	return EOK;
+}
+
+static void tcp_cconn_destroy(tcp_cconn_t *cconn)
+{
+	list_remove(&cconn->lclient);
+	free(cconn);
+}
+
+static int tcp_clistener_create(tcp_client_t *client, tcp_conn_t *conn,
+    tcp_clst_t **rclst)
+{
+	tcp_clst_t *clst;
+	sysarg_t id;
+
+	clst = calloc(1, sizeof(tcp_clst_t));
+	if (clst == NULL)
+		return ENOMEM;
+
+	/* Allocate new ID */
+	id = 0;
+	list_foreach (client->clst, lclient, tcp_clst_t, clst) {
+		if (clst->id >= id)
+			id = clst->id + 1;
+	}
+
+	clst->id = id;
+	clst->client = client;
+	clst->conn = conn;
+
+	list_append(&clst->lclient, &client->clst);
+	*rclst = clst;
+	return EOK;
+}
+
+static void tcp_clistener_destroy(tcp_clst_t *clst)
+{
+	list_remove(&clst->lclient);
+	free(clst);
+}
+
+static int tcp_cconn_get(tcp_client_t *client, sysarg_t id,
+    tcp_cconn_t **rcconn)
+{
+	list_foreach (client->cconn, lclient, tcp_cconn_t, cconn) {
+		if (cconn->id == id) {
+			*rcconn = cconn;
+			return EOK;
+		}
+	}
+
+	return ENOENT;
+}
+
+static int tcp_clistener_get(tcp_client_t *client, sysarg_t id,
+    tcp_clst_t **rclst)
+{
+	list_foreach (client->clst, lclient, tcp_clst_t, clst) {
+		if (clst->id == id) {
+			*rclst = clst;
+			return EOK;
+		}
+	}
+
+	return ENOENT;
+}
+
+
+static int tcp_conn_create_impl(tcp_client_t *client, inet_ep2_t *epp,
+    sysarg_t *rconn_id)
+{
+	tcp_conn_t *conn;
+	tcp_cconn_t *cconn;
+	tcp_sock_t local;
+	tcp_sock_t remote;
+	inet_addr_t local_addr;
+	int rc;
+	tcp_error_t trc;
+	char *slocal;
+	char *sremote;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_impl");
+
+	/* Fill in local address? */
+	if (inet_addr_is_any(&epp->local.addr)) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_impl: "
+		    "determine local address");
+		rc = inet_get_srcaddr(&epp->remote.addr, 0, &local_addr);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_impl: "
+			    "cannot determine local address");
+			return rc;
+		}
+	} else {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_impl: "
+		    "local address specified");
+		local_addr = epp->local.addr;
+	}
+
+	/* Allocate local port? */
+	if (epp->local.port == 0) {
+		epp->local.port = 49152; /* XXX */
+	}
+
+	local.addr = local_addr;
+	local.port = epp->local.port;
+	remote.addr = epp->remote.addr;
+	remote.port = epp->remote.port;
+
+	inet_addr_format(&local_addr, &slocal);
+	inet_addr_format(&remote.addr, &sremote);
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create: local=%s remote=%s",
+	    slocal, sremote);
+
+	trc = tcp_uc_open(&local, &remote, ap_active, tcp_open_nonblock, &conn);
+	if (trc != TCP_EOK)
+		return EIO;
+
+	rc = tcp_cconn_create(client, conn, &cconn);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		tcp_conn_delete(conn);
+		return ENOMEM;
+	}
+
+	/* XXX Is there a race here (i.e. the connection is already active)? */
+	tcp_uc_set_cb(conn, &tcp_service_cb, cconn);
+
+//	assoc->cb = &udp_cassoc_cb;
+//	assoc->cb_arg = cassoc;
+
+	*rconn_id = cconn->id;
+	return EOK;
+}
+
+static int tcp_conn_destroy_impl(tcp_client_t *client, sysarg_t conn_id)
+{
+	tcp_cconn_t *cconn;
+	int rc;
+
+	rc = tcp_cconn_get(client, conn_id, &cconn);
+	if (rc != EOK) {
+		assert(rc == ENOENT);
+		return ENOENT;
+	}
+
+	tcp_uc_close(cconn->conn);
+	tcp_cconn_destroy(cconn);
+	return EOK;
+}
+
+static int tcp_listener_create_impl(tcp_client_t *client, inet_ep_t *ep,
+    sysarg_t *rlst_id)
+{
+	tcp_conn_t *conn;
+	tcp_clst_t *clst;
+	tcp_sock_t local;
+	int rc;
+	tcp_error_t trc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_create_impl");
+
+	local.addr = ep->addr;
+	local.port = ep->port;
+
+	trc = tcp_uc_open(&local, NULL, ap_passive, 0, &conn);
+	if (trc != TCP_EOK)
+		return EIO;
+
+	rc = tcp_clistener_create(client, conn, &clst);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		tcp_conn_delete(conn);
+		return ENOMEM;
+	}
+
+//	assoc->cb = &udp_cassoc_cb;
+//	assoc->cb_arg = cassoc;
+
+	*rlst_id = clst->id;
+	return EOK;
+}
+
+static int tcp_listener_destroy_impl(tcp_client_t *client, sysarg_t lst_id)
+{
+	tcp_clst_t *clst;
+	int rc;
+
+	rc = tcp_clistener_get(client, lst_id, &clst);
+	if (rc != EOK) {
+		assert(rc == ENOENT);
+		return ENOENT;
+	}
+
+//	tcp_uc_close(cconn->conn);
+	tcp_clistener_destroy(clst);
+	return EOK;
+}
+
+static int tcp_conn_send_fin_impl(tcp_client_t *client, sysarg_t conn_id)
+{
+	tcp_cconn_t *cconn;
+	int rc;
+
+	rc = tcp_cconn_get(client, conn_id, &cconn);
+	if (rc != EOK) {
+		assert(rc == ENOENT);
+		return ENOENT;
+	}
+
+	(void) cconn;
+	/* XXX TODO */
+	return EOK;
+}
+
+static int tcp_conn_push_impl(tcp_client_t *client, sysarg_t conn_id)
+{
+	tcp_cconn_t *cconn;
+	int rc;
+
+	rc = tcp_cconn_get(client, conn_id, &cconn);
+	if (rc != EOK) {
+		assert(rc == ENOENT);
+		return ENOENT;
+	}
+
+	(void) cconn;
+	/* XXX TODO */
+	return EOK;
+}
+
+static int tcp_conn_reset_impl(tcp_client_t *client, sysarg_t conn_id)
+{
+	tcp_cconn_t *cconn;
+	int rc;
+
+	rc = tcp_cconn_get(client, conn_id, &cconn);
+	if (rc != EOK) {
+		assert(rc == ENOENT);
+		return ENOENT;
+	}
+
+	tcp_uc_abort(cconn->conn);
+	return EOK;
+}
+
+static int tcp_conn_send_impl(tcp_client_t *client, sysarg_t conn_id,
+    void *data, size_t size)
+{
+	tcp_cconn_t *cconn;
+	int rc;
+
+	rc = tcp_cconn_get(client, conn_id, &cconn);
+	if (rc != EOK)
+		return rc;
+
+	rc = tcp_uc_send(cconn->conn, data, size, 0);
+	if (rc != EOK)
+		return rc;
+
+	return EOK;
+}
+
+static int tcp_conn_recv_impl(tcp_client_t *client, sysarg_t conn_id,
+    void *data, size_t size, size_t *nrecv)
+{
+	tcp_cconn_t *cconn;
+	xflags_t xflags;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl()");
+
+	rc = tcp_cconn_get(client, conn_id, &cconn);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - conn not found");
+		return rc;
+	}
+
+	rc = tcp_uc_receive(cconn->conn, data, size, nrecv, &xflags);
+	if (rc != EOK) {
+		switch (rc) {
+		case TCP_EAGAIN:
+			log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - EAGAIN");
+			return EAGAIN;
+		default:
+			log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - trc=%d", rc);
+			return EIO;
+		}
+	}
+
+	return EOK;
+}
+
+static void tcp_callback_create_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_callback_create_srv()");
+
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	client->sess = sess;
+	async_answer_0(iid, EOK);
+}
+
+static void tcp_conn_create_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	inet_ep2_t epp;
+	sysarg_t conn_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_create_srv()");
+
+	if (!async_data_write_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(inet_ep2_t)) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(callid, &epp, size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+
+	rc = tcp_conn_create_impl(client, &epp, &conn_id);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+
+	async_answer_1(iid, EOK, conn_id);
+}
+
+static void tcp_conn_destroy_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	sysarg_t conn_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_destroy_srv()");
+
+	conn_id = IPC_GET_ARG1(*icall);
+	rc = tcp_conn_destroy_impl(client, conn_id);
+	async_answer_0(iid, rc);
+}
+
+static void tcp_listener_create_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	inet_ep_t ep;
+	sysarg_t lst_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_create_srv()");
+
+	if (!async_data_write_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	if (size != sizeof(inet_ep_t)) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	rc = async_data_write_finalize(callid, &ep, size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+
+	rc = tcp_listener_create_impl(client, &ep, &lst_id);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		return;
+	}
+
+	async_answer_1(iid, EOK, lst_id);
+}
+
+static void tcp_listener_destroy_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	sysarg_t lst_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_listener_destroy_srv()");
+
+	lst_id = IPC_GET_ARG1(*icall);
+	rc = tcp_listener_destroy_impl(client, lst_id);
+	async_answer_0(iid, rc);
+}
+
+static void tcp_conn_send_fin_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	sysarg_t conn_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_send_fin_srv()");
+
+	conn_id = IPC_GET_ARG1(*icall);
+	rc = tcp_conn_send_fin_impl(client, conn_id);
+	async_answer_0(iid, rc);
+}
+
+static void tcp_conn_push_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	sysarg_t conn_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_push_srv()");
+
+	conn_id = IPC_GET_ARG1(*icall);
+	rc = tcp_conn_push_impl(client, conn_id);
+	async_answer_0(iid, rc);
+}
+
+static void tcp_conn_reset_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	sysarg_t conn_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_reset_srv()");
+
+	conn_id = IPC_GET_ARG1(*icall);
+	rc = tcp_conn_reset_impl(client, conn_id);
+	async_answer_0(iid, rc);
+}
+
+static void tcp_conn_send_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	sysarg_t conn_id;
+	void *data;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_send_srv())");
+
+	/* Receive message data */
+
+	if (!async_data_write_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	if (size > MAX_MSG_SIZE) {
+		async_answer_0(callid, EINVAL);
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	data = malloc(size);
+	if (data == NULL) {
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+	}
+
+	rc = async_data_write_finalize(callid, data, size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		free(data);
+		return;
+	}
+
+	conn_id = IPC_GET_ARG1(*icall);
+
+	rc = tcp_conn_send_impl(client, conn_id, data, size);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		free(data);
+		return;
+	}
+
+	async_answer_0(iid, EOK);
+	free(data);
+}
+
+static void tcp_conn_recv_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	sysarg_t conn_id;
+	size_t size, rsize;
+	void *data;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_srv()");
+
+	conn_id = IPC_GET_ARG1(*icall);
+
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	size = max(size, 16384);
+	data = malloc(size);
+	if (data == NULL) {
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	rc = tcp_conn_recv_impl(client, conn_id, data, size, &rsize);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		free(data);
+		return;
+	}
+
+	rc = async_data_read_finalize(callid, data, size);
+	if (rc != EOK) {
+		async_answer_0(iid, rc);
+		free(data);
+		return;
+	}
+
+	async_answer_1(iid, EOK, rsize);
+	free(data);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_srv(): OK");
+}
+
+static void tcp_conn_recv_wait_srv(tcp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	sysarg_t conn_id;
+	size_t size, rsize;
+	void *data;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv()");
+
+	conn_id = IPC_GET_ARG1(*icall);
+
+	if (!async_data_read_receive(&callid, &size)) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - data_receive failed");
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+
+	size = min(size, 16384);
+	data = malloc(size);
+	if (data == NULL) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - allocation failed");
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	rc = tcp_conn_recv_impl(client, conn_id, data, size, &rsize);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - recv_impl failed rc=%d", rc);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		free(data);
+		return;
+	}
+
+	rc = async_data_read_finalize(callid, data, size);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv - finalize failed");
+		async_answer_0(iid, rc);
+		free(data);
+		return;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv(): rsize=%zu", size);
+	async_answer_1(iid, EOK, rsize);
+	free(data);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_wait_srv(): OK");
+}
+
+#include <mem.h>
+static void tcp_client_conn(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	tcp_client_t client;
+
+	/* Accept the connection */
+	async_answer_0(iid, EOK);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn() - client=%p",
+	    &client);
+
+	memset(&client, 0, sizeof(client));
+	client.sess = NULL;
+	list_initialize(&client.cconn);
+	list_initialize(&client.clst);
+//	list_initialize(&client.crcv_queue);
+
+	while (true) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn: wait req");
+		ipc_call_t call;
+		ipc_callid_t callid = async_get_call(&call);
+		sysarg_t method = IPC_GET_IMETHOD(call);
+
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_client_conn: method=%d",
+		    (int)method);
+		if (!method) {
+			/* The other side has hung up */
+			async_answer_0(callid, EOK);
+			return;
+		}
+
+		switch (method) {
+		case TCP_CALLBACK_CREATE:
+			tcp_callback_create_srv(&client, callid, &call);
+			break;
+		case TCP_CONN_CREATE:
+			tcp_conn_create_srv(&client, callid, &call);
+			break;
+		case TCP_CONN_DESTROY:
+			tcp_conn_destroy_srv(&client, callid, &call);
+			break;
+		case TCP_LISTENER_CREATE:
+			tcp_listener_create_srv(&client, callid, &call);
+			break;
+		case TCP_LISTENER_DESTROY:
+			tcp_listener_destroy_srv(&client, callid, &call);
+			break;
+		case TCP_CONN_SEND_FIN:
+			tcp_conn_send_fin_srv(&client, callid, &call);
+			break;
+		case TCP_CONN_PUSH:
+			tcp_conn_push_srv(&client, callid, &call);
+			break;
+		case TCP_CONN_RESET:
+			tcp_conn_reset_srv(&client, callid, &call);
+			break;
+		case TCP_CONN_SEND:
+			tcp_conn_send_srv(&client, callid, &call);
+			break;
+		case TCP_CONN_RECV:
+			tcp_conn_recv_srv(&client, callid, &call);
+			break;
+		case TCP_CONN_RECV_WAIT:
+			tcp_conn_recv_wait_srv(&client, callid, &call);
+			break;
+		default:
+			async_answer_0(callid, ENOTSUP);
+			break;
+		}
+	}
+	log_msg(LOG_DEFAULT, LVL_DEBUG,
+	    "tcp_client_conn TERMINATED ***************************");
+}
+
+int tcp_service_init(void)
+{
+	int rc;
+	service_id_t sid;
+
+	async_set_client_connection(tcp_client_conn);
+
+	rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering server.");
+		return EIO;
+	}
+
+	rc = loc_service_register(SERVICE_NAME_TCP, &sid);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed registering service.");
+		return EIO;
+	}
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tcp/service.h
===================================================================
--- uspace/srv/net/tcp/service.h	(revision 779541be88a7692182b80bde3b7fae49f3832431)
+++ uspace/srv/net/tcp/service.h	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 Jiri Svoboda
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup tcp
+ * @{
+ */
+/** @file HelenOS service implementation
+ */
+
+#ifndef SERVICE_H
+#define SERVICE_H
+
+extern int tcp_service_init(void);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tcp/tcp.c
===================================================================
--- uspace/srv/net/tcp/tcp.c	(revision 99ea91b21f443b2767081c1f285807bb248b0682)
+++ uspace/srv/net/tcp/tcp.c	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -48,4 +48,5 @@
 #include "pdu.h"
 #include "rqueue.h"
+#include "service.h"
 #include "std.h"
 #include "tcp.h"
@@ -192,9 +193,9 @@
 	}
 
-//	rc = tcp_sock_init();
-//	if (rc != EOK) {
-//		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing socket service.");
-//		return ENOENT;
-//	}
+	rc = tcp_service_init();
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed initializing service.");
+		return ENOENT;
+	}
 
 	return EOK;
Index: uspace/srv/net/tcp/tcp_type.h
===================================================================
--- uspace/srv/net/tcp/tcp_type.h	(revision 99ea91b21f443b2767081c1f285807bb248b0682)
+++ uspace/srv/net/tcp/tcp_type.h	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -96,5 +96,6 @@
 	TCP_EINVPREC,
 	/* Security/compartment not allowed */
-	TCP_EINVCOMP
+	TCP_EINVCOMP,
+	TCP_EAGAIN
 } tcp_error_t;
 
@@ -154,4 +155,10 @@
 typedef void (*tcp_cstate_cb_t)(tcp_conn_t *, void *);
 
+/** Connection callbacks */
+typedef struct {
+	void (*cstate_change)(tcp_conn_t *, void *);
+	void (*recv_data)(tcp_conn_t *, void *);
+} tcp_cb_t;
+
 /** Connection */
 struct tcp_conn {
@@ -159,8 +166,8 @@
 	link_t link;
 
-	/** Connection state change callback function */
-	tcp_cstate_cb_t cstate_cb;
+	/** Connection callbacks function */
+	tcp_cb_t *cb;
 	/** Argument to @c cstate_cb */
-	void *cstate_cb_arg;
+	void *cb_arg;
 
 	/** Connection identification (local and foreign socket) */
@@ -318,7 +325,35 @@
 } tcp_pdu_t;
 
-typedef struct {
+/** TCP client connection */
+typedef struct tcp_cconn {
+	/** Connection */
+	tcp_conn_t *conn;
+	/** Connection ID for the client */
+	sysarg_t id;
+	/** Client */
+	struct tcp_client *client;
+	link_t lclient;
+} tcp_cconn_t;
+
+/** TCP client listener */
+typedef struct tcp_clst {
+	/** Connection */
+	tcp_conn_t *conn;
+	/** Listener ID for the client */
+	sysarg_t id;
+	/** Client */
+	struct tcp_client *client;
+	/** Link to tcp_client_t.clst */
+	link_t lclient;
+} tcp_clst_t;
+
+/** TCP client */
+typedef struct tcp_client {
+	/** Client callbac session */
 	async_sess_t *sess;
-//	socket_cores_t sockets;
+	/** Client's connections */
+	list_t cconn; /* of tcp_cconn_t */
+	/** Client's listeners */
+	list_t clst;
 } tcp_client_t;
 
@@ -358,5 +393,4 @@
 } tcp_sock_lconn_t;
 
-
 #endif
 
Index: uspace/srv/net/tcp/tqueue.c
===================================================================
--- uspace/srv/net/tcp/tqueue.c	(revision 99ea91b21f443b2767081c1f285807bb248b0682)
+++ uspace/srv/net/tcp/tqueue.c	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -285,5 +285,5 @@
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG,
-	    "tcp_transmit_segment(f:(%u),l:(%u), %p)",
+	    "tcp_transmit_segment(l:(%u),f:(%u), %p)",
 	    sp->local.port, sp->foreign.port, seg);
 	
Index: uspace/srv/net/tcp/ucall.c
===================================================================
--- uspace/srv/net/tcp/ucall.c	(revision 99ea91b21f443b2767081c1f285807bb248b0682)
+++ uspace/srv/net/tcp/ucall.c	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -188,4 +188,6 @@
 	/* Wait for data to become available */
 	while (conn->rcv_buf_used == 0 && !conn->rcv_buf_fin && !conn->reset) {
+		tcp_conn_unlock(conn);
+		return TCP_EAGAIN;
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_receive() - wait for data");
 		fibril_condvar_wait(&conn->rcv_buf_cv, &conn->lock);
@@ -294,11 +296,11 @@
 }
 
-void tcp_uc_set_cstate_cb(tcp_conn_t *conn, tcp_cstate_cb_t cb, void *arg)
-{
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_set_ctate_cb(%p, %p, %p)",
+void tcp_uc_set_cb(tcp_conn_t *conn, tcp_cb_t *cb, void *arg)
+{
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_set_cb(%p, %p, %p)",
 	    conn, cb, arg);
 
-	conn->cstate_cb = cb;
-	conn->cstate_cb_arg = arg;
+	conn->cb = cb;
+	conn->cb_arg = arg;
 }
 
Index: uspace/srv/net/tcp/ucall.h
===================================================================
--- uspace/srv/net/tcp/ucall.h	(revision 99ea91b21f443b2767081c1f285807bb248b0682)
+++ uspace/srv/net/tcp/ucall.h	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -50,5 +50,5 @@
 extern void tcp_uc_status(tcp_conn_t *, tcp_conn_status_t *);
 extern void tcp_uc_delete(tcp_conn_t *);
-extern void tcp_uc_set_cstate_cb(tcp_conn_t *, tcp_cstate_cb_t, void *);
+extern void tcp_uc_set_cb(tcp_conn_t *, tcp_cb_t *, void *);
 
 /*
Index: uspace/srv/net/udp/service.c
===================================================================
--- uspace/srv/net/udp/service.c	(revision 99ea91b21f443b2767081c1f285807bb248b0682)
+++ uspace/srv/net/udp/service.c	(revision 779541be88a7692182b80bde3b7fae49f3832431)
@@ -160,5 +160,6 @@
 	udp_sock_t remote;
 	int rc;
-	char *la, *ra;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_impl");
 
 	local.addr = epp->local.addr;
@@ -166,13 +167,4 @@
 	remote.addr = epp->remote.addr;
 	remote.port = epp->remote.port;
-
-	la = (char *)"xxx";
-	ra = (char *)"yyy";
-
-	(void) inet_addr_format(&epp->local.addr, &la);
-	(void) inet_addr_format(&epp->remote.addr, &ra);
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_create_impl la=%s ra=%s",
-	    la, ra);
 
 	assoc = udp_assoc_new(&local, &remote, NULL, NULL);
